2008-09-29 10 views
7

Tengo un programa en C++ que representa una cabecera TCP como una estructura:Conseguir diferente tamaño de la cabecera, cambiando el tamaño de ventana

#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; 
} 

Si me quedo este programa me da el tamaño de esta cabecera como 24 bytes, lo que no es el tamaño que esperaba Si cambio el tipo del campo "wWindow" a "unsigned int wWindow: 16", que tiene el mismo número de bits que un short sin signo, el programa me dice que el tamaño de la estructura ahora es de 20 bytes, el tamaño correcto. ¿Por qué es esto?

Estoy usando Microsoft Visual Studio 2005 con SP1 en una máquina x86 de 32 bits.

Respuesta

2

Ver esta pregunta: Why isn't sizeof for a struct equal to the sum of sizeof of each member?.

Creo que el compilador da una pista para deshabilitar el relleno cuando se usa la sintaxis "unsigned int wWindow: 16".

Además, tenga en cuenta que un corto no garantiza 16 bits. La garantía es que: 16 bits < = tamaño de un corto < = tamaño de un int.

+0

@andy: +1, podría incluir #pragma push/pop en el parámetro pack para ayudarlo. – user7116

+0

Mike B tiene la respuesta correcta a continuación. Consulte también este debate: http://groups.google.com/group/microsoft.public.dotnet.languages.vc/browse_frm/thread/7ea120d16c49611d/bdf918a490a6d61a?lnk=st&q=bitfield#bdf918a490a6d61a – ChrisN

6

Porque el compilador está empaquetando su campo de bits en una entidad int de 32 bits, no en una entidad de 16 bits.

En general, debe evitar los campos de bits y usar otras constantes de manifiesto (enumeraciones o lo que sea) con enmascaramiento y desplazamiento de bits explícitos para acceder a los 'subcampos' en un campo.

He aquí una razón por la que se deben evitar los campos de bits: no son muy portátiles entre los compiladores, incluso para la misma plataforma. de la norma C99 (hay un texto similar en el estándar C90):

una aplicación puede asignar cualquier unidad de almacenamiento direccionable lo suficientemente grande como para contener un campo de bits. Si queda suficiente espacio , un campo de bit que inmediatamente sigue a otro campo de bit en una estructura debe empaquetarse en bits adyacentes de la misma unidad. Si queda espacio insuficiente, si un campo de bit que no se ajusta se pone en la siguiente unidad o se superpone a unidades adyacentes es definido por la implementación. El orden de asignación de campos de bit dentro de una unidad (de orden alto a bajo orden o de bajo pedido a orden superior) es definido por la implementación. La alineación de la unidad de almacenamiento direccionable es no especificada.

No se puede garantizar si un campo de bit 'abarcará' un límite int o no y no se puede especificar si un bitfield comienza en el extremo inferior del int o el extremo superior del int (esto es independientemente de si el procesador es big-endian o little-endian).

+0

Me pregunto si alguna vez alguien definirá un medio portátil para especificar bitfields, al menos en hardware compatible con POSIX, p. usando una sintaxis de algo como "uInt32_t thing1, thing2; field1: thing1.28.4; field2: thing1.0.28; field3: thing2.12.20;" ¿etc.? Tales declaraciones de campo no asignarían espacio, pero accederían a los campos previamente asignados. He visto compiladores con nomenclatura para superponer banderas de bits en variables declaradas estáticamente; Me pregunto si alguno tiene una sintaxis para superponer campos de bits en otros campos de estructura. – supercat

0

El compilador está rellenando el miembro de estructura no de bitfield con la alineación de palabra nativa de 32 bits. Para arreglar esto, haga #pragma pack (0) antes de struct y #pragma pack() después.

+0

# paquete pragma (0) no cambió el comportamiento. –

0

Los límites de la estructura en la memoria pueden ser rellenados por el compilador dependiendo del tamaño y el orden de los campos.

0

No es un experto en C/C++ cuando se trata de embalaje. Pero imagino que hay una regla en la especificación que dice que cuando un campo no bit sigue a un campo de bits, debe estar alineado en el límite de la palabra, independientemente de si encaja o no en el espacio restante. Al convertirlo en un bitvector explícito, estás evitando este problema.

De nuevo, esto es una especulación con un toque de experiencia.

0

Interesante - Creo que "WORD" se evaluaría como "sin signo corto", por lo que tendría ese problema en más de un lugar.

También tenga en cuenta que tendrá que tratar con problemas endian en cualquier valor de más de 8 bits.

0

Estás viendo valores diferentes debido a las reglas de empaquetado del compilador. Puede ver reglas específicas para visual studio here.

Cuando tiene una estructura que debe empaquetarse (o cumplir con algunos requisitos de alineación específicos), debe usar la opción #pragma pack(). Para su código, puede usar #pragma pack (0) que alineará todos los miembros de la estructura en los límites de bytes. Luego puede usar #pragma pack() para restablecer el empaque de la estructura a su estado predeterminado. Puede ver más información en el paquete pragma here.

4

Su serie de campos de bit "unsigned int: xx" utiliza hasta 16 de los 32 bits en un int. Los otros 16 bits (2 bytes) están ahí, pero no utilizados. Esto es seguido por el corto sin firmar, que está en un límite int, y luego un WORD, que está alineado a lo largo de un límite int, lo que significa que hay 2 bytes de relleno entre ellos.

Cuando cambia a "unsigned int wWindow: 16", en lugar de ser un corto separado, el compilador utiliza las partes no utilizadas del campo de bits anterior, por lo que no hay desperdicio, ni corto, ni relleno después del corto, por lo tanto salvar cuatro bytes.

0

Creo que Mike B lo hizo bien, pero no está del todo claro. Cuando pides "corto", está alineado en un límite de 32 bits. Cuando pides int: 16, no lo es. Entonces int: 16 se ajusta justo después de los campos de ebit, mientras que el corto omite 2 bytes y comienza en el siguiente bloque de 32 bits.

El resto de lo que dice es perfectamente aplicable: el campo de bits nunca se debe usar para codificar una estructura visible externamente, porque no hay garantía de cómo se asignan. En el mejor de los casos, pertenecen a programas integrados donde guardar un byte es importante. E incluso allí, no puede usarlos para controlar realmente bits en puertos mapeados en memoria.

Cuestiones relacionadas