offsetof



  • Ich bin gerade über das Makro offsetof (aus cstddef ) gestolpert.

    Wieso gibt es im MSVC 2 versch. Versionen für 32 und 64bit?

    //64bit
    #define offsetof(s,m)   (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )
    
    //32bit
    #define offsetof(s,m)   (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
    

    Ich verstehe nicht ganz, warum der ptrdiff_t cast nur in der 64bit Version notwendig sein sollte...

    Ich hätte nur die 64bit Variante genommen weil ich den ptrdiff_t cast für notwendig halte - vll möchte mir ja wer sagen, wieso er nicht notwendig sein sollte oder wieso er nur im 64bit mode notwendig sein sollte...

    Ich weiß nicht, ob es so geschrieben schneller ersichtlich ist, was passiert (ich hab jedenfalls ein bisschen gebraucht, bis ich mich durch die hintereinander-version durchgefrickelt hatte):

    #	ifndef offsetof2
    #		define offsetof2(s,m)                                 \
    			(size_t)(	                                      \
    				(ptrdiff_t)                                    \
    					&reinterpret_cast<const volatile char&>(   \
    						(((s *)NULL)->m)                       \
    					)                                          \
    			)
    #	endif
    

    Es scheint auch zu funktionieren (bsp ist hier geklaut: http://www.cplusplus.com/reference/clibrary/cstddef/offsetof/)

    struct mystruct
    {
    	char singlechar;
    	char arraymember[10];
    	char anotherchar;
    };
    
    #include <iostream>
    
    int main()
    {
    	std::cout << offsetof2(mystruct,singlechar) << std::endl;
    	std::cout << offsetof2(mystruct,arraymember)<< std::endl;
    	std::cout << offsetof2(mystruct,anotherchar)<< std::endl;
    }
    

    Ausgabe ist (zumindest im Debug-Mode im MSVC) gleich wie in dem oben genannten Bsp.

    bb



  • In 32bit sind ja size_t (unsigned int) und 'const volatile char *' jeweils 32 bit groß, d.h. die Konvertierung klappt einwandfrei.

    Wie ist denn size_t unter 64bit definiert (denn dort ist ja '(unsigned) int' auch nur 32 bit groß) während 'ptrdiff_t' ja 64 bit groß sein muß (d.h. long)?

    Dies dient m.E. also nur um Konvertierungswarnungen zu unterdrücken (char * -> unsigned int).

    Dies bedeutet aber auch, daß dann offsetof nur auf Strukturen kleiner als 4.2 GByte angewendet werden kann! (Scherz)

    Oder ist size_t auf 64bit auch 64 bit lang (d.h. unsigned long)?



  • size_t ist genau so groß wie ptrdiff_t - nämlich immer genau so groß, wie die Registergröße...

    'const volatile char *' jeweils 32 bit groß sollte meines erachtens egal sein - außerdem sollte ein Pointer auf 64bit-Systemen logischerweise auch 64bit groß sein... Im Prinzip steht ja am Ende dann so etwas da:

    //64bit
    char* x = /*...*/;
    ptrdiff_t y = ptrdiff_t(x);
    return size_t(y);
    
    //32bit
    char* x = /*...*/;
    return size_t(x);
    

    Nachdem ich jetzt ein wenig rumprobiert habe, bin ich iwie zu dem Schluss gekommen, dass der ptrdiff_t Zwischenschritt weder bei 32 noch bei 64bit notwendig wäre:

    #include <limits>
    
    void myassert(bool cond)
    {
    	if(!cond)
    		throw 1;
    }
    
    template<typename size_t, typename ptrdiff_t>
    void my_proof(size_t addr)
    {
    	ptrdiff_t y = (ptrdiff_t)addr;
    	size_t z1 = (size_t)addr;
    	size_t z2 = (size_t)y;
    	myassert(z1 == z2);
    }
    
    void proof(unsigned long long addr)
    {
    	typedef long long ptrdiff_t;
    	typedef unsigned long long size_t;
    	my_proof<size_t, ptrdiff_t> (addr);
    }
    
    void proof(unsigned int addr)
    {
    	typedef int ptrdiff_t;
    	typedef unsigned int size_t;
    	my_proof<size_t, ptrdiff_t> (addr);
    }
    
    #include <iostream>
    
    int main()
    {
    	//32bit
    	for(unsigned int addr(0), max(std::numeric_limits<unsigned int>::max()); addr != max; ++addr)
    	{
    		if(!(addr%1000000000))
    			std::cout << addr << " / " << max << std::endl;
    
    		proof(addr);
    	}
    	proof(std::numeric_limits<unsigned int>::max());
    
    	//64bit
    	const unsigned long long step = 100000000000000000; //ich hatte keine Lust Tage zu warten ;o)
    	for(unsigned long long addr(0), max((std::numeric_limits<unsigned long long>::max()/step)*step); addr != max; addr += step/100000)
    	{
    		if(!(addr%step))
    			std::cout << addr << " / " << max << std::endl;
    
    		proof(addr);
    	}
    	proof(std::numeric_limits<unsigned long long>::max());
    }
    

    Ist etwas chaotisch, aber sollte dennoch verständlich sein ^^

    bb


Log in to reply