[VC++] short en char 4 bytes lang?

Pagina: 1
Acties:

  • mvds
  • Registratie: November 2000
  • Laatst online: 11:01

mvds

Totally awesome!

Topicstarter
Ik heb een probleem onder MS Visual Studio 6 met service pack 5. Ik gebruik het volgende struct:

code:
1
2
3
4
5
6
7
8
typedef struct
{
    unsigned short opcode;
    unsigned long version;
    char uid[24];           // username
    char pwd[24];           // password
    unsigned char _dummy;
} C_LOGIN;


Het is een kale applicatie en deze struct word gebruikt om in te loggen op een server, als ik met de debugger opvraag wat er verstuurd word zie ik dat de grootte van de struct 60 bytes is. Dit zou dus eigenlijk maar 55 mogen zijn.

Het probleem is dat de unsigned short en de unsigned char als 4 bytes verstuurd worden ipv 2 bytes voor de unsigned short en 1 byte voor de unsigned char.

Ik denk dat dit ligt aan een instelling van visual studio, ik zou geen andere verklaring kunnen vinden. Ook al worden er geen waarden toegekend aan de struct, toch is de grootte 60 bytes.

Wie kan me hiermee helpen?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 15:49
Dit fenomeen heet member alignment; de compiler plaatst de verschillende onderdelen van een structure zo, dat ze op een 64-bits grens in het geheugen liggen. De processor vraagt geheugen immers op in blokjes van (meestal) 64 bits. Hierdoor kan het voorkomen, dat de processor voor een integer (32-bits) twee keer 64 bits moet opvragen, omdat de ene helft van de data toevallig in het ene blok geheugen en de andere helft in het volgende ligt. De compiler ruilt hier geheugenruimte in voor een verbeterde executiesnelheid.

Je kunt de alignment in Visual Studio instellen onder de project settings, onder C/C++, Code Generation, Struct member alignment. Die wil jij dan waarschijnlijk op 1 byte zetten. Er zijn ook compiler directives om de alignment alleen tijdelijk (en in je source file) aan te passen, maar die weet ik niet zo een-twee-drie te vinden; daar kan Google je vast wel meer over vertellen.

  • mvds
  • Registratie: November 2000
  • Laatst online: 11:01

mvds

Totally awesome!

Topicstarter
Ok, thanks. Dit zal vast wel helpen.

  • mvds
  • Registratie: November 2000
  • Laatst online: 11:01

mvds

Totally awesome!

Topicstarter
Inderdaad, perfect. Het werkt nu prima. Ik wist wel dat het aan een instelling lag, wist alleen niet welke.

Dude, je hebt een +1 Behulpzaam verdient. :)

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Moet er wel een beetje mee oppassen als je je code bv ooit naar een sparc oid wilt porten. Daar is het namelijk niet alleen "wat executie snelheid", maar zorgt een miss-aligned read voor een bus error, resultaat core dump :P
Dit stukje C crashed bv op solaris/sparc

code:
1
2
3
4
5
6
7
8
int main() {
    char buf[3];       // neem aan buf aligned op bv 4byte
    unsigned short i;  // 2 bytes groot

    i = *(unsigned short*)(&buf[1]); // !! bus error !!

    return 0;
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 31-08 15:26

.oisyn

Moderator Devschuur®

Demotivational Speaker



Niet helemaal correct
MSVC aligned de members van de structure op sizeof (member) boundaries. Een long wordt dus gealigned op 4 bytes, een short op 2 en een char op 1. De instelling van struct member alignment in MSVC stelt het grootste type wat gealigned moet worden op zijn eigen size, types groter dan deze size worden gealigned op de ingestelde size

Je kunt het ook in je source forceren met de #parma pack () directive:
C++:
1
2
3
4
5
#pragma pack(1)
struct Blaat
{
    // je members hier
};


echter, vanaf dit stuk code wordt alles op 1 byte gealigned, wat misschien niet is wat je wilt. Dus daarvoor kun je gebruik maken van een soort stack:
C++:
1
2
3
4
5
6
#pragma pack (push, 1)
struct Blaat
{
    // members
};
#pragma pack (pop)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Juicy
  • Registratie: December 2000
  • Laatst online: 31-08 07:06
Zoijar schreef op 02 oktober 2002 @ 21:48:
Moet er wel een beetje mee oppassen als je je code bv ooit naar een sparc oid wilt porten. Daar is het namelijk niet alleen "wat executie snelheid", maar zorgt een miss-aligned read voor een bus error, resultaat core dump :P
Dit stukje C crashed bv op solaris/sparc

code:
1
2
3
4
5
6
7
8
int main() {
    char buf[3];       // neem aan buf aligned op bv 4byte
    unsigned short i;  // 2 bytes groot

    i = *(unsigned short*)(&buf[1]); // !! bus error !!

    return 0;
}
Maar als je dit soort code schrijft dan vraag je ook om problemen ...

-


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Juicy schreef op 02 oktober 2002 @ 21:58:
[...]


Maar als je dit soort code schrijft dan vraag je ook om problemen ...
Het was maar een simpel voorbeeldje om een bus error te forceren. Het kan ook gebeuren in minder duidelijke situaties. Bv een net packet dat je verstuurd en dat aankomt als void*. Als je die void* dan naar bv een struct net_header_t* zou reinterpret casten, en je struct begint met een char, bv een 8bit ID, en dan een 32bit checksum heb je een bus error op het punt dat je het checksum uitleest. Zijn niet echt fijne fouten...

En ik zei het eigenlijk zomaar omdat het onderwerp alignment boven kwam, en dit misschien nuttig comentaar was omdat volgens mij niet iedereen dit weet. Mij kostte het in ieder geval aardig wat tijd de eerste keer dat deze fout opdook :)

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 15:49
Jij bent wel op een kruistocht om mij te verbeteren vandaag, hè? ;)
MSVC aligned de members van de structure op sizeof (member) boundaries.
Ah; je hebt gelijk. Daar dacht ik even niet bij na. De topicstarter zei al dat z'n ints op 32-bit gealigned werden terwijl de default-instelling 64-bits is.

Alignment op de eigen grootte is natuurlijk ook al afdoende om te garanderen dat voor het opzoeken van een enkel lid niet meer geheugen zal worden opgevraagd dan noodzakelijk. (Voorwaarde is wel dat de grootte van de elementen een deler zijn van de in het systeem gebruikte busbreedte; met getallen als 1, 2 en 4 bytes gaat dat wel goed.)
Je kunt het ook in je source forceren met de #pragma pack () directive:
Die bedoelde ik inderdaad. Nog steeds jammer dat daar geen compiler standaard voor is; het maakt het gebruik van structs voor het lezen/schrijven van records in binaire bestanden een stuk minder makkelijk (want niet portable).

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 31-08 15:26

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op 02 oktober 2002 @ 22:05:
[...]
Jij bent wel op een kruistocht om mij te verbeteren vandaag, hè? ;)
mmja dat was precies wat ik dacht toen ik jouw quotte... "he kut, weer soultaker". No hard feelings :Y)
Die bedoelde ik inderdaad. Nog steeds jammer dat daar geen compiler standaard voor is; het maakt het gebruik van structs voor het lezen/schrijven van records in binaire bestanden een stuk minder makkelijk (want niet portable).


is idd jammer, maar zoals Zoijar al aangaf: niet elke cpu kan zomaar op 'random' adressen bij zijn data. Een 32 bits int op een niet-4-byte-boundary definieren zou dan bijvoorbeeld leiden naar een crash... Maar dat is natuurlijk ook wel weer op te lossen door de compiler code te laten genereren dat (bijvoorbeeld) de 4 bytes afzonderlijk inleest en daar de originele int weer uit haalt

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Dan heb je ook nog byte order enzo, verschillende grootte types. Allemaal wel makkelijk op te lossen als je er rekening mee houd hoor, maar toch... vraag me af hoeveel file formats/readers echt portable zijn...

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 15:49
Dat valt meestal wel te ondervangen door een aantal conversiefuncties op je ingelezen structures los te laten, vergelijkbaar met de standaard functies voor netwerkadressen (htons, htonl, etc.). De code en sich zou dan portable moeten zijn, al kan de implementatie van die hulpfuncties op verschillende platforms verschillen.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

ja, exact hetzelfde. htons() enzo. Gewoon hetzelfde als bij bv een tcp implementatie. Maar ik vraag me af of mensen dat ook doen by binary files, of dat er gedacht wordt dat het alleen voor netwerken nodig is.
Maar ach, we hebben tegenwoordig XML :)
Pagina: 1