Pergunta

Eu tenho um programa C ++ representando um cabeçalho TCP como uma estrutura:

#include "stdafx.h"

/*  TCP HEADER

    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

*/

typedef struct {        // RFC793
    WORD         wSourcePort;
    WORD         wDestPort;
    DWORD        dwSequence;
    DWORD        dwAcknowledgment;
    unsigned int byReserved1:4;
    unsigned int byDataOffset:4;
    unsigned int fFIN:1;
    unsigned int fSYN:1;
    unsigned int fRST:1;
    unsigned int fPSH:1;
    unsigned int fACK:1;
    unsigned int fURG:1;
    unsigned int byReserved2:2;
    unsigned short wWindow;
    WORD         wChecksum;
    WORD         wUrgentPointer;
} TCP_HEADER, *PTCP_HEADER;


int _tmain(int argc, _TCHAR* argv[])
{
    printf("TCP header length: %d\n", sizeof(TCP_HEADER));
    return 0;
}

Se eu executar este programa, recebo o tamanho desse cabeçalho como 24 bytes, que não é do tamanho que eu esperava. Se eu alterar o tipo de campo "WWindow" para "não assinado int window: 16", que tem o mesmo número de bits que um curta não assinado, o programa me diz que o tamanho da estrutura agora é de 20 bytes, o tamanho correto. Por que é isso?

Estou usando o Microsoft Visual Studio 2005 com o SP1 em uma máquina X86 de 32 bits.

Foi útil?

Solução

Veja esta pergunta: Por que o tamanho de uma estrutura não é igual à soma do tamanho de cada membro? .

Acredito que o compilador toma uma dica para desativar o preenchimento quando você usa a sintaxe "não assinada int window: 16".

Além disso, observe que um curto não é garantido em 16 bits. A garantia é que: 16 bits <= tamanho de um tamanho curto <= de um int.

Outras dicas

Porque o compilador está embalando seu campo de bits em um INT de 32 bits, não uma entidade de 16 bits.

Em geral, você deve evitar o Bitfields e usar outras constantes manifestas (enumes ou qualquer outra coisa) com mascaramento explícito de bits e mudanças para acessar os 'subfields' em um campo.

Aqui está uma das razões pelas quais o Bitfields deve ser evitado - eles não são muito portáteis entre os compiladores, mesmo para a mesma plataforma. A partir do padrão C99 (há uma redação semelhante no padrão C90):

Uma implementação pode alocar qualquer unidade de armazenamento endereçável grande o suficiente para manter um campo de bits. Se houver espaço suficiente, um campo de bits que segue imediatamente outro campo de bits em uma estrutura deve ser embalado em bits adjacentes da mesma unidade. Se o espaço insuficiente permanecer, se um campo de bits que não se encaixa é colocado na próxima unidade ou sobreposições adjacentes são definidas por implementação. A ordem de alocação de campos de bits dentro de uma unidade (ordem de alta a ordem ou ordem baixa a alta ordem) é definida pela implementação. O alinhamento da unidade de armazenamento endereçável não é especificado.

Você não pode garantir se um campo de um bit 'abrange' um limite int ou não e não pode especificar se um campo de bits começa no final do INT ou na extremidade alta do INT (isso é independente de se o processador está big-endian ou pouco endiano).

Sua série de campos Bitfields "não assinados Int: XX" usam apenas 16 dos 32 bits em um int. Os outros 16 bits (2 bytes) estão lá, mas não utilizados. Isto é seguido pelo curto não assinado, que está em um limite int, e depois uma palavra, que está alinhada em um limite int, o que significa que há 2 bytes de preenchimento entre eles.

Quando você muda para "não assinado int window: 16", em vez de ser um curto separado, o compilador usa as partes não utilizadas do campo de bits anteriores, para que sem desperdício, sem curto e sem preenchimento após o curto, portanto você economiza quatro bytes.

O compilador está preenchendo o membro da Struct não bitfield para alinhamento de palavras nativas de 32 bits. Para corrigir isso, faça o #Pragma Pack (0) antes do pacote STREST e #PRAGMA () depois.

Os limites da estrutura na memória podem ser acolchoados pelo compilador, dependendo do tamanho e da ordem dos campos.

Não é um especialista em C/C ++ quando se trata de embalagem. Mas imagino que exista uma regra na especificação que diz que, quando um campo não bitfield segue um campo de bit, ela deve estar alinhada na palavra limite, independentemente de se encaixar ou não no espaço restante. Ao torná -lo um BitVector explícito, você está evitando esse problema.

Novamente, isso é especulação com um toque de experiência.

Interessante - eu pensaria que "palavra" avaliaria para "curto não assinado", então você teria esse problema em mais de um lugar.

Esteja ciente de que você precisará lidar com problemas do Endian em qualquer valor acima de 8 bits.

Eu acho que Mike B acertou, mas não perfeitamente claro. Quando você pede "curto", está alinhado em limites de 32 bits. Quando você pede Int: 16, não é. Então, Int: 16 se encaixa logo após os campos de EBIT, enquanto pula mais bytes e começa no próximo bloco de 32 bits.

O restante do que ele está dizendo é perfeitamente aplicável - o campo de bits nunca deve ser usado para codificar uma estrutura externamente visível, porque não há garantia de como eles são alocados. Na melhor das hipóteses, eles pertencem a programas incorporados, onde a economia de um byte é importante. E mesmo lá, você não pode usá-los para realmente controlar bits em portas mapeadas de memória.

Você está vendo valores diferentes devido às regras de embalagem do compilador. Você pode ver regras específicas para o Visual Studio aqui.

Quando você tem uma estrutura que deve ser embalada (ou aderir a alguns requisitos de alinhamento específicos), você deve usar a opção #Pragma Pack (). Para o seu código, você pode usar o #Pragma Pack (0) que alinhará todos os membros da estrutura nos limites de byte. Você pode usar o #Pragma Pack () para redefinir a embalagem da estrutura para o estado padrão. Você pode ver mais informações sobre o pacote Pragma aqui.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top