A std::vector<char>
contém um ponteiro para algum buffer alocado no heap.
Considerando problemas de alinhamento de memória, o buffer alocado retornado pela alocação de memória tem garantia de estar alinhado corretamente apenas para o tipo char
ou será alinhado a algum valor comum, como 4 ou 8 bytes? (Ou não é especificado?)
Para explicar por meio de exemplo, o alocador padrão é new
. A chamada new
pode retornar um endereço de memória arbitrário. Mas ele é realmente arbitrário?
- Talvez ele seja alinhado em 8 bytes em máquinas de 64 bits?
- Talvez esteja alinhado ao tipo
T
usado emstd::vector<T>
? (Esta parece ser a opção mais provável.) - Talvez o endereço retornado seja arbitrário e possa ter qualquer valor. (Não acho que isso esteja correto.)
- Mais alguma coisa?
O motivo desta pergunta é que quero saber se posso reinterpretar com segurança os bytes contidos neste bloco de memória como outros tipos, por exemplo float
, double
, uint64_t
, etc.
Indo na outra direção, a situação é mais óbvia. (Eu acho - corrija-me.)
Por exemplo, se solicitássemos um std::vector<uint64_t>
, o ponteiro retornado pela alocação de memória e mantido pelo std::vector
objeto seria alinhado em 8 bytes.
Portanto, seria seguro ler e escrever dados como:
char*
- Alinhado corretamente (alinhado de 4 bytes)
float
- Alinhado corretamente (8 bytes)
double
- Alinhado corretamente (2 bytes)
unsigned short int
- ...etc
Entretanto, se a alocação para std::vector<char>
puder retornar um endereço de memória de 1025
(decimal), então claramente não há garantia de que ele será alinhado corretamente em qualquer deslocamento se reinterpretado como qualquer tipo maior que 1 byte de largura.
Acho que meu entendimento está correto aqui, mas é claro que você pode me avisar nos comentários se alguma coisa não fizer sentido ou não se encaixar corretamente.
Editar:
Posso ter encontrado uma resposta para isso, mas não tenho certeza de como interpretar o que li.
De:
Esta função é necessária para retornar um ponteiro adequadamente alinhado para apontar para um objeto do tamanho solicitado.
Mas o que isso significa?
Para um objeto de largura de 1 byte, isso aparentemente sugere que qualquer valor de endereço é aceitável.
Para um objeto de largura de 2 bytes, isso sugeriria que qualquer endereço de valor par seria aceitável.
Para 4 bytes, qualquer múltiplo de 4. E para 8 bytes, qualquer múltiplo de 8.
Mas e quanto a outros valores? Não há nenhuma instrução para carregar um valor de 3 bytes da memória em x86, até onde eu saiba. O que acontece se você solicitar new[T]
where T
is a 3 byte wide struct
?
Embora tecnicamente não pareça garantido, tenho quase certeza de que nenhuma implementação fornecerá um ponteiro subalinhado de um
std::vector<char>
.O padrão
operator new
retorna uma memória alinhada a__STDCPP_DEFAULT_NEW_ALIGNMENT__
, e embora eu não consiga encontrar nenhuma garantia de que isso seja>= alignof(std::max_align_t)
, qualquer implementação sensata faria dessa maneira.Há também uma versão
operator new
que usastd::align_val_t
, e embora sua intenção seja oferecer suporte a alinhamentos maiores, não há garantia de questd::allocator
não a chame para alinhamentos menores também, embora uma implementação sensata provavelmente não faria isso, ou se fizesse, a chamada ainda garantiria pelo menos__STDCPP_DEFAULT_NEW_ALIGNMENT__
o alinhamento.Uma grande parte do motivo pelo qual espero que isso funcione na prática é que
std::align_val_t
e o overalignedoperator new
foi adicionado relativamente recentemente, no C++17. Antes disso, você tinha que ter__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(std::max_align_t)
for paranew
funcionar corretamente, estd::vector<char>
tinha que lhe dar um ponteiro alinhado pelo menos para__STDCPP_DEFAULT_NEW_ALIGNMENT__
, então duvido que as implementações vão abandonar essa garantia de repente.