Eu li que a ordem dos campos de bits em uma estrutura é específica da plataforma. E se eu usar diferentes opções de empacotamento específicas do compilador, esses dados de garantia serão armazenados na ordem correta conforme são escritos? Por exemplo:
struct Message
{
unsigned int version : 3;
unsigned int type : 1;
unsigned int id : 5;
unsigned int data : 6;
} __attribute__ ((__packed__));
Em um processador Intel com o compilador GCC, os campos foram dispostos na memória conforme são mostrados. Message.version
foram os primeiros 3 bits no buffer e os Message.type
seguiram. Se eu encontrar opções de empacotamento de struct equivalentes para vários compiladores, isso será multiplataforma?
c++
c
bit-manipulation
endianness
bit
Dewald
fonte
fonte
Respostas:
Não, não será totalmente portátil. As opções de embalagem para estruturas são extensões e não são totalmente portáteis. Além disso, C99 §6.7.2.1, parágrafo 10 diz: "A ordem de alocação de campos de bits dentro de uma unidade (ordem alta para ordem inferior ou ordem inferior para ordem alta) é definida pela implementação."
Mesmo um único compilador pode definir o campo de bits de forma diferente, dependendo do endianness da plataforma de destino, por exemplo.
fonte
packed
impor ordenando: stackoverflow.com/questions/1756811/... como impor ordenação bit: stackoverflow.com/questions/6728218/gcc-compiler-bit-orderOs campos de bits variam amplamente de compilador para compilador, desculpe.
Com o GCC, as máquinas big endian exibem as partes big end primeiro e as máquinas little endian exibem as partes little endian primeiro.
K&R diz "Membros de campo adjacentes [bit-] de estruturas são empacotados em unidades de armazenamento dependentes de implementação em uma direção dependente de implementação. Quando um campo seguindo outro campo não caberá ... ele pode ser dividido entre unidades ou a unidade pode ser preenchido. Um campo sem nome de largura 0 força este preenchimento ... "
Portanto, se você precisa de um layout binário independente da máquina, você deve fazer isso sozinho.
Esta última declaração também se aplica a campos não-bit devido ao preenchimento - entretanto, todos os compiladores parecem ter alguma maneira de forçar o empacotamento de bytes de uma estrutura, como vejo que você já descobriu para o GCC.
fonte
Bitfields devem ser evitados - eles não são muito portáveis entre compiladores, mesmo para a mesma plataforma. da norma C99 6.7.2.1/10 - "Especificadores de estrutura e união" (há formulação semelhante na norma C90):
Você não pode garantir se um campo de bits irá 'ultrapassar' um limite interno ou não e você não pode especificar se um campo de bits começa na extremidade inferior do int ou no extremo superior do int (isto é independente se o processador é big-endian ou little-endian).
Prefira bitmasks. Use inlines (ou mesmo macros) para definir, limpar e testar os bits.
fonte
_BIT_FIELDS_LTOH
e_BIT_FIELDS_HTOL
endianness está falando sobre ordens de bytes, não ordens de bits. Hoje em dia , é 99% certo que os pedidos de bits são fixos. No entanto, ao usar bitfields, endianness deve ser levado em conta. Veja o exemplo abaixo.
#include <stdio.h> typedef struct tagT{ int a:4; int b:4; int c:8; int d:16; }T; int main() { char data[]={0x12,0x34,0x56,0x78}; T *t = (T*)data; printf("a =0x%x\n" ,t->a); printf("b =0x%x\n" ,t->b); printf("c =0x%x\n" ,t->c); printf("d =0x%x\n" ,t->d); return 0; } //- big endian : mips24k-linux-gcc (GCC) 4.2.3 - big endian a =0x1 b =0x2 c =0x34 d =0x5678 1 2 3 4 5 6 7 8 \_/ \_/ \_____/ \_____________/ a b c d // - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2 a =0x2 b =0x1 c =0x34 d =0x7856 7 8 5 6 3 4 1 2 \_____________/ \_____/ \_/ \_/ d c b a
fonte
Na maioria das vezes, provavelmente, mas não aposte muito nisso, porque se você estiver errado, perderá muito.
Se você realmente precisa ter informações binárias idênticas, você precisará criar bitfields com bitmasks - por exemplo, você usa um curto sem sinal (16 bits) para Message e depois faz coisas como versionMask = 0xE000 para representar os três bits superiores.
Existe um problema semelhante com o alinhamento dentro das estruturas. Por exemplo, Sparc, PowerPC e CPUs 680x0 são big-endian, e o padrão comum para compiladores Sparc e PowerPC é alinhar membros de estrutura em limites de 4 bytes. No entanto, um compilador que usei para 680x0 apenas alinhou em limites de 2 bytes - e não havia opção para alterar o alinhamento!
Portanto, para algumas estruturas, os tamanhos em Sparc e PowerPC são idênticos, mas menores em 680x0, e alguns dos membros estão em deslocamentos de memória diferentes dentro da estrutura.
Este era um problema com um projeto em que trabalhei, porque um processo de servidor em execução no Sparc consultaria um cliente e descobriria que era big-endian, e assumiria que poderia apenas espalhar structs binários na rede e o cliente poderia lidar com isso. E funcionou bem em clientes PowerPC e travou bastante em clientes 680x0. Não escrevi o código e demorou um pouco para encontrar o problema. Mas foi fácil consertar depois que fiz.
fonte
Obrigado @BenVoigt pelo seu comentário muito útil começando
Source Linux faz usar um campo de bits para corresponder a uma estrutura externa: /usr/include/linux/ip.h tem esse código para o primeiro byte de um datagrama IP
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif
No entanto, à luz do seu comentário, estou desistindo de tentar fazer isso funcionar para o campo de bits multibyte frag_off .
fonte
Claro que a melhor resposta é usar uma classe que lê / escreve campos de bits como um fluxo. Usar a estrutura de campo de bits C não é garantido. Sem mencionar que é considerado pouco profissional / preguiçoso / estúpido usar isso na codificação do mundo real.
fonte