2017-04-19 2 views
1

je le code suivant:taille struct Bitfield différente entre gcc et MSFT CL

#include <cstdint> 

#pragma pack(1) 
using MyType_t = union { 
    uint8_t buffer[16]; 
    struct { 
     uint64_t a   : 55; 
     uint64_t b   : 24; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
}; 
#pragma pack() 

int main() 
{ 
    return sizeof(MyType_t); 
} 

Je reçois un résultat différent entre gcc \ clang et Visual C++ (Microsoft CL), quand je compare le code assembleur dans le compilateur Explorer et je suis le suivant:

bruit (std = C++ 11 -O3)

main:         # @main 
     mov  eax, 16 
     ret 

x86-64 gcc 6.3 (O3)

main: 
     mov  eax, 16 
     ret 

x86-64 CL 19 2017 RTW (Ox)

main PROC 
     mov  eax, 24 
     ret  0 
main ENDP 

est-il Visual C++ bogue du compilateur ou il est un comportement indéfini ?

+3

Presque tout avec bitfeilds est un comportement défini par l'implémentation. Très probablement ce que vous voyez est que gcc/clang est intelligent et optimise les 4 champs en 2 'uint64_t's où msvs en ajoute un de plus donc vous n'avez pas un bitfiled qui s'étend sur deux' uint64_t's – NathanOliver

Répondre

1

Je crois qu'il s'agit d'un comportement indéfini. @NathanOliver a la bonne réponse: GCC et Clang couvrent les deux valeurs uint64_t. Il y a un coût quand vous le lisez bien: voir cet exemple de code très similaire sur le Compiler Explorer où GCC doit maintenant lire deux champs et faire des calculs pour donner la deuxième valeur.

1

Si vous voulez les deux layous pour être cohérent entre les deux compilateurs vous pouvez utiliser la directive de GCC __attribute__((ms_struct)) pour avoir utiliser l'algorithme de mise en page de bitfield de Microsoft:

using MyType_t 
= union { 
    uint8_t buffer[16]; 
    struct __attribute__((ms_struct)) { 
     uint64_t a   : 55; 
     uint64_t b   : 24; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
}; 

Vous pouvez également utiliser l'option -mms-bitfields avec GCC, mais c'est une option de changement ABI qui pourrait casser d'autres codes.

Si vous voulez aller dans l'autre sens, et forcer le compilateur de Microsoft à utiliser la disposition bitfield de GCC, je ne pense pas qu'il y ait un attribut ou une option pour le faire. Vous devez modifier votre code et diviser le membre b pour qu'il ne franchisse pas la limite de 64 bits. Quelque chose comme:

#pragma pack(1) 
typedef union { 
    uint8_t buffer[16]; 
#ifdef USE_GCC_BITFIELDS 
    struct __attribute__((gcc_struct)) { 
     uint64_t a   : 55; 
     uint64_t b   : 24; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
    uint64_t get_a() { return fields.a; } 
    uint64_t get_b() { return fields.b; } 
    uint64_t get_c() { return fields.c; } 
    uint64_t get_d() { return fields.d; } 
#elif defined(USE_MS_BITFIELDS) 
    struct { 
     uint64_t a   : 55; 
     uint64_t bl  : 9; 
     uint64_t bh  : 15; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
    uint64_t get_a() { return fields.a; } 
    uint64_t get_b() { return fields.bl | (fields.bh << 9); } 
    uint64_t get_c() { return fields.c; } 
    uint64_t get_d() { return fields.d; } 
#else /* portable code that should work anywhere */ 
    unsigned long long get_ull(int i) { 
     typedef unsigned long long ull; unsigned char *p = buffer + i; 
     return (ull) p[0] | ((ull) p[1] << 8) | ((ull) p[2] << 16) | ((ull) p[3] << 24) 
      | ((ull) p[4] << 32) | ((ull) p[5] << 40) | (((ull) p[6]) << 48) 
      | ((ull) p[7] << 56); } 
    unsigned long long get_a() { return get_ull(0) & ((1ULL << 55) - 1); } 
    unsigned get_b() { return (buffer[6] >> 7) | (buffer[7] << 1) 
      | (buffer[8] << 9) | ((buffer[9] & 0x7F) << 17); } 
    unsigned get_c() { return buffer[9] >> 7; } 
    unsigned long long get_d() { return get_ull(8) >> 16; } 
#endif 

} MyType_t; 
#pragma pack() 
+0

Merci, je ne ' Je veux ajouter un remplissage, je veux l'utiliser sur la mémoire. – Baget