Pregunta

Tengo un programa C ++ que representa un encabezado TCP como una estructura:

#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 ejecuto este programa, obtengo el tamaño de este encabezado como 24 bytes, que no es el tamaño que esperaba. Si cambio el tipo de campo & Quot; wWindow & Quot; a & "; unsigned int wWindow: 16 &"; que tiene el mismo número de bits que un corto 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.

¿Fue útil?

Solución

Vea esta pregunta: ¿Por qué sizeof para una estructura no es igual a la suma de sizeof de cada miembro? .

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

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

Otros consejos

Debido a que el compilador está empaquetando su campo de bits en un int de 32 bits, no en una entidad de 16 bits.

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

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

  

Una implementación puede asignar cualquier   unidad de almacenamiento direccionable lo suficientemente grande   para sostener un campo de bits. Si hay suficiente espacio   permanece, un campo de bits que inmediatamente   sigue otro campo de bits en un   estructura se embalará en   bits adyacentes de la misma unidad. Si   queda espacio insuficiente, ya sea   campo de bits que no encaja se pone   en la siguiente unidad o se superpone   unidades adyacentes es   definido por la implementación. El orden de   asignación de campos de bits dentro de una unidad   (orden alta a orden baja o orden baja   de alto orden) es   definido por la implementación. El alineamiento   de la unidad de almacenamiento direccionable es   sin especificar.

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

Su serie de " unsigned int: xx " los bitfields usan solo 16 de los 32 bits en un int. Los otros 16 bits (2 bytes) están ahí, pero sin usar. Esto es seguido por el corto sin signo, que está en un límite int, y luego una WORD, que está alineada 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 usa las partes no utilizadas del campo de bits anterior, por lo que no hay desperdicio, no short y no relleno después del corto, por lo tanto, guarda cuatro bytes.

El compilador rellena el miembro de estructura que no es de campo de bits a una alineación de palabras nativas de 32 bits. Para solucionar esto, haga #pragma pack (0) antes de la estructura y #pragma pack () después.

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

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

Nuevamente, esto es especulación con un toque de experiencia.

Interesante: creo que " WORD " evaluaría a " unsigned short " ;, por lo que tendría ese problema en más de un lugar.

También tenga en cuenta que tendrá que lidiar con problemas endian en cualquier valor superior a 8 bits.

Creo que Mike B lo hizo bien, pero no del todo claro. Cuando solicita & Quot; short & Quot ;, se alinea en el límite de 32 bits. Cuando pides int: 16, no lo es. Entonces int: 16 encaja justo después de los campos de bits, 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 debe usarse 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 en los que es importante guardar un byte. E incluso allí, no puede usarlos para controlar bits en puertos mapeados en memoria.

Está viendo valores diferentes debido a las reglas de empaquetado del compilador. Puede ver las reglas específicas de Visual Studio aquí .

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 el paquete #pragma (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 estructura a su estado predeterminado. Puede ver más información sobre el paquete pragma aquí .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top