lang lang innretting problem (MSVC vs. GCC)

stemmer
1

Jeg skriver C kryssplattform bibliotek, men etter hvert har jeg fått feil i mine unittests, men bare på Windows-maskiner. Jeg har sporet problemet og fant det er relatert til justering av strukturer (jeg bruker matriser med strukturer for å holde data for flere lignende objekter). Problemet er: memset (sizeof (struct)) og innstillings strukturer etter tur inn produsere forskjellig byte-to-byte resultat og derfor memcmp () returnerer ikke lik resultat.

Her kode for å illustrere:

#include <stdio.h>
#include <string.h>

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

S1 s1, s2;

int main()
{
    printf(%d %d\n, sizeof(S1), sizeof(S2));

    memset(&s1, 0xFF, sizeof(S1));
    memset(&s2, 0x00, sizeof(S1));

    s1.a = 0LL; s1.b = 0;

    if (0 == memcmp(&s1, &s2, sizeof(S1)))
        printf(Equal\n);
    else
        printf(Not equal\n);

    return 0;
}

Denne koden med MSVC 2003 @ Windows produserer følgende resultat:

16 8
Not equal

Men den samme koden med GCC 3.3.6 @ Linux fungerer som forventet:

12 8
Equal

Dette gjør min enhet-testing veldig hardt.

Er jeg forstår riktig at MSVC bruker størrelsen største innfødte type (lang lang) for å bestemme innretting å strukturere?

Kan noen gi meg råd hvordan kan jeg endre koden min for å gjøre det mer robust mot dette merkelige justering problemet? I min virkelige kode jeg jobber med matriser av strukturer via generiske pekere til å utføre memset / memcmp og jeg er vanligvis ikke vet nøyaktig type, jeg har bare sizeof (struct) verdi.

Publisert på 04/03/2009 klokken 15:41
kilden bruker
På andre språk...                            


6 svar

stemmer
4

Enheten test forventning er galt. Den (eller de kode det tester) bør ikke skanne konstruksjonens buffer byte-for-bitgruppe. For byte-nøyaktige data koden skal skape en byte buffer eksplisitt på stabelen eller på haugen og fylle det med ekstrakter fra hvert medlem. Ekstraktene kan oppnås i CPU-endianness-uavhengig måte ved hjelp av riktig operasjon skift mot de heltallige verdier, og å konvertere resultatet av byte type, slik som (unsigned char).

BTW, skriver tekstutdraget fortid s2. Du kan fikse det ved å endre denne

memset(&s2, 0x00, sizeof(S1));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S1)))

til dette,

memset(&s2, 0x00, sizeof(S2));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S2)))

men resultatet er teknisk "udefinert" fordi justeringen av medlemmene i strukturene er kompilatoren-spesifikke.

Svarte 27/03/2009 kl. 03:18
kilden bruker

stemmer
2

GCC Manuell:

Legg merke til at justeringen av enhver struct eller union typen kreves av ISO C-standarden til å være minst en perfekt multiplum av minste felles multiplum av de justeringer av alle medlemmene av struct eller forening i spørsmålet.

Dessuten innfører dette typisk et element av vattering (dvs. fyll bytes for å ha strukturen i tråd). Du kan bruke #pragmamed et argument for packed. Merk, #pragmas er ikke en bærbar måte å arbeide på. Dessverre er dette også om den eneste måten å jobbe på i ditt tilfelle.

Referanser: Her GCC på struktur justering. MSDN struktur innretting.

Svarte 04/03/2009 kl. 15:49
kilden bruker

stemmer
1

Merk at dette ikke er et 'rart' justering problem. MSVC har valgt for å sikre at struct er justert på en 64-bit grense, siden den har en 64-bit medlem slik at den legger noen polstring ved slutten av struct for å sørge for at rekker av disse gjenstander vil ha hvert element riktig innrettet. Jeg er faktisk overrasket over at GCC ikke gjør det samme.

Jeg er nysgjerrig på hva du er enhetstesting gjør som treffer en ulempe med dette - det meste av tiden justering av strukturmedlemmer er ikke nødvendig med mindre du trenger å matche et binært filformat eller en wire protokoll eller du virkelig trenger å redusere minne som brukes av en struktur (spesielt anvendes i integrerte systemer). Uten å vite hva du prøver å gjøre i testene jeg tror ikke et godt forslag kan gis. Pakking strukturen kan være en løsning, men det kommer til noen kostnader - ytelse (spesielt på ikke-Intel-plattformer) og portabilitet (hvordan struct pakking er satt opp er kan være forskjellig fra kompilatoren til kompilatoren). Disse kan ikke noe for deg, men det kan være en bedre måte å løse problemet i ditt tilfelle.

Svarte 04/03/2009 kl. 16:21
kilden bruker

stemmer
1

Du kan enten gjøre noe sånt

#ifdef _MSC_VER
#pragma pack(push, 16)
#endif

/* your struct defs */

#ifdef _MSC_VER
#pragma pack(pop)
#endif

å gi en kompilator direktiv tvinger justering

Eller gå inn i prosjektet alternativer og endre standard struct justering [under kode Generation]

Svarte 04/03/2009 kl. 15:45
kilden bruker

stemmer
1

Det vi har gjort er brukt #pragma pakke for å spesifisere hvor store gjenstander bør være:

#pragma pack(push, 2)

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

#pragma pack(pop)

Hvis du gjør dette, vil strukturene være av samme størrelse på begge plattformer.

Svarte 04/03/2009 kl. 15:44
kilden bruker

stemmer
0

Struktur polstring for 64-bit-verdier er forskjellig på forskjellige kompilatorer. Jeg har sett forskjeller mellom selv mellom GCC mål.

Merk at eksplisitt padding til 64-bit innretting vil bare skjule problemet. Det vil komme tilbake hvis du begynner naivt hekkende strukturer, fordi kompilatorer vil fortsatt uenige om naturlig justering av indre strukturer.

Svarte 05/03/2009 kl. 14:00
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more