Question

J'ai un programme C ++ représentant un en-tête TCP en tant que struct:

#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 je lance ce programme, la taille de cet en-tête est de 24 octets, ce qui n’est pas la taille que j’attendais. Si je change le type du champ & Quot; wWindow & Quot; to " unsigned int wWindow: 16 " ;, qui a le même nombre de bits qu'un short non signé, le programme me dit que la taille de la structure est maintenant de 20 octets, la taille correcte. Pourquoi est-ce?

J'utilise Microsoft Visual Studio 2005 avec SP1 sur un ordinateur 32 bits x86.

Était-ce utile?

La solution

Voir la question suivante: Pourquoi sizeof pour une structure n'est-il pas égal à la somme de sizeof de chaque membre? .

Je pense que le compilateur utilise un indice pour désactiver le remplissage lorsque vous utilisez le & "unsigned int wWindow: 16 &"; syntaxe.

Notez également qu’un court-circuit n’est pas garanti sur 16 bits. La garantie est que: 16 bits & Lt; = taille d’un court & Lt; = taille d’un int.

Autres conseils

Parce que le compilateur compresse votre champ binaire dans un int 32 bits, pas une entité 16 bits.

En général, vous devriez éviter les champs de bits et utiliser d'autres constantes manifestes (enum ou autre) avec masquage et décalage de bits explicites pour accéder aux "sous-champs" d'un champ.

Voici une des raisons pour lesquelles les champs de bits doivent être évités: ils ne sont pas très portables entre les compilateurs, même pour la même plate-forme. de la norme C99 (le libellé est similaire dans la norme C90):

  

Une implémentation peut allouer tout   unité de stockage adressable assez grande   tenir un bitfield. Si assez d'espace   reste, un champ de bits qui immédiatement   suit un autre champ de bits dans un   la structure doit être emballée dans   bits adjacents de la même unité. Si   il reste peu d'espace, qu'il s'agisse d'un   champ de bits qui ne correspond pas est mis   dans l'unité suivante ou se chevauche   unités adjacentes est   défini par la mise en œuvre. L'ordre de   attribution de champs de bits dans une unité   (ordre élevé à ordre bas ou ordre bas   à ordre élevé) est   défini par la mise en œuvre. L'alignement   de l'unité de stockage adressable est   non spécifié.

Vous ne pouvez pas garantir qu'un champ de bits "étend" une frontière int ou non et vous ne pouvez pas spécifier si un champ débute à l'extrémité inférieure de l'entier ou à l'extrémité supérieure de l'int le processeur est big-endian ou little-endian).

Votre série de " unsigned int: xx " les champs de bits n'utilisent que 16 des 32 bits d'un int. Les 16 autres bits (2 octets) sont là, mais non utilisés. Ceci est suivi du raccourci non signé, qui se trouve sur une limite int, puis d'un mot, aligné sur une limite int, ce qui signifie qu'il y a 2 octets de bourrage entre eux.

Lorsque vous passez à & "unsigned int wWindow: 16 &" ;, au lieu d'être un court-métrage séparé, le compilateur utilise les parties inutilisées du champ précédent, donc aucun gaspillage, aucun court le remplissage après le court-métrage, vous économisez donc quatre octets.

Le compilateur compresse le membre struct non-bitfield vers un alignement de mots natif 32 bits. Pour résoudre ce problème, faites #pragma pack (0) avant la structure et #pragma pack () après.

Les limites des structures en mémoire peuvent être complétées par le compilateur en fonction de la taille et de l'ordre des champs.

Pas un expert en C / C ++ en matière d’emballage. Mais j'imagine que la spécification contient une règle qui stipule que lorsqu'un champ non binaire suit un champ binaire, il doit être aligné sur la limite de mot, qu'il soit ou non inséré dans l'espace restant. En faisant un bitvector explicite, vous évitez ce problème.

Encore une fois, c’est de la spéculation avec une touche d’expérience.

Intéressant - Je pense que " WORD " serait évalué à & "; unsigned short &" ;, de sorte que vous auriez ce problème à plusieurs endroits.

Sachez également que vous devez gérer les problèmes de finalité dans les valeurs supérieures à 8 bits.

Je pense que Mike B a bien compris, mais pas très clairement. Lorsque vous demandez & Quot; court & Quot ;, il est aligné sur la limite de 32 bits. Lorsque vous demandez int: 16, ce n'est pas. So int: 16 convient juste après les champs ebit, tandis que court saute 2 octets et commence au bloc 32 bits suivant.

Le reste de ce qu'il dit est parfaitement applicable - le champ de bits ne doit jamais être utilisé pour coder une structure visible de l'extérieur, car rien ne garantit leur attribution. Au mieux, ils appartiennent à des programmes intégrés dans lesquels la sauvegarde d'un octet est importante. Et même là, vous ne pouvez pas les utiliser pour contrôler des bits dans des ports mappés en mémoire.

Vous voyez des valeurs différentes à cause des règles d'empaquetage du compilateur. Vous pouvez consulter les règles spécifiques à Visual Studio ici .

Lorsque vous avez une structure qui doit être compactée (ou respecter certaines exigences d’alignement spécifiques), vous devez utiliser l’option #pragma pack (). Pour votre code, vous pouvez utiliser #pragma pack (0) qui alignera tous les membres de la structure sur les limites d’octets. Vous pouvez ensuite utiliser #pragma pack () pour réinitialiser la structure de la structure à son état par défaut. Vous pouvez voir plus d'informations sur le pack pragma ici .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top