What's in a long: 64 bit? Of toch 32?

Sunday 04 April 2010, 22:32:00 | software dev

Vanaf eind jaren 80 toen ik mijn Amiga 500 kocht, heb ik eigenlijk altijd gewerkt op een 32 bits operating systeem en CPU. Pas recentelijk heb ik een PC met daarin een Intel Core2 CPU die 64 bit ondersteunt, en daarop Windows 7 64-bits. Ik heb ook een Mac Mini met een Core2 CPU (ook 64 bits dus) met Mac OS X 10.6.

Onlangs ben ik weer eens in C aan het programmeren (ugh, maar het gaat om een aanpassing aan Python dus dat maakt het weer goed). Het wordt dus eens tijd om rekening te gaan houden met wat dat nou betekent, 64 bits, als je aan het programmeren bent.

Ik kwam er namelijk achter dat er een subtiel verschijnsel optreedt waar ik tot nu toe geen weet van had. Kort samengevat gaat het om de lengte van een long. Het blijkt dat op een 64 bits machine deze niet altijd 64 bits hoeft te zijn. Lees verder voor de details.

Hoe lang is een long dan? 32 bits op een 32 bits systeem en 64 bits op een 64 bits systeem, zou je zeggen. Maar nee dus.

Vanwaar deze post? Omdat me dit opeens opviel:

Mac-Htpc:~ irmen$ python -c "import sys; print sys.maxint"
9223372036854775807

[E:\]c:\Python26_64\python.exe -c "import sys; print sys.maxint"
2147483647

De bovenste is de standaard Python op de Mac, wat een 64 bits versie is. Volgens verwachting: maxint is 64 bits. De onderste is de 64 bits Python voor Windows, maar sys.maxint is daar zeker geen 64 bits o_o

En dan wilde ik vanzelfsprekend weten waarom dat niet hetzelfde is!

Een Python int is intern gerepresenteerd door een C long. Dus om eens te kijken wat het gedrag is van verschillende compilers op verschillende machines.

De source van mijn test programma:

#include <stdio.h>
int main(int argc, char** argv)
{
        printf("sizeof int=%d\n",(int)sizeof(int));
        printf("sizeof long=%d\n",(int)sizeof(long));
        printf("sizeof long long=%d\n",(int)sizeof(long long));
        printf("sizeof pointer=%d\n",(int)sizeof(void*));
        return 0;
}

Compilers op Windows 7: gcc van mingw-64 en cl van de Microsoft Windows 7 SDK x64. Compiler op Mac OS X: gcc van de Apple developer tools.

Resultaten op Mac OS X:

Mac-Htpc:~ irmen$ gcc -m64 test.c
Mac-Htpc:~ irmen$ ./a.out
sizeof int=4
sizeof long=8
sizeof long long=8
sizeof pointer=8

int is nog 32 bits maar long is netjes 8 bytes (64 bits). Net als pointers. En long long is ook 64 bits (verplicht volgens de standaard).

Resultaten op Windows 7:

E:\>gcc -m64 test.c -o test.exe

E:\>test.exe
sizeof int=4
sizeof long=4
sizeof long long=8
sizeof pointer=8

E:\>cl test.c
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64
   .... 
E:\>test.exe
sizeof int=4
sizeof long=4
sizeof long long=8
sizeof pointer=8

"sizeof long=4"! Ongeacht de compiler is een long op Windows dus ook gewoon 32 bits! (Een pointer en een long long zijn wel 64 bits)

Maar waarom is dat dan?!

Nou blijkt er zoiets te bestaan als een 64 bits data model. En die van Windows verschilt dus van de meeste andere 64-bits operating systemen. Windows gebruikt het "LLP64" model, en de meeste anderen gebruiken het "LP64" model. Het verschil is dus precies of een long 32- of 64-bits is...

Meer hard core achtergrond info in deze whitepaper over Unix of hier dus op Wikipedia. Voor beide modellen zijn voor- en nadelen trouwens.

Nog meer hardcore info over portability issues op 64 bits systemen. En een duidelijk voorbeeld waarom ik eigenlijk gezworen had nooit meer C te programmeren >)