2009-02-03 2 views
21

Die meisten erfahrenen Programmierer weiß, Datenabgleich für Programmleistung wichtig ist. Ich habe gesehen, dass ein Programmierer ein Programm geschrieben hat, das eine größere Puffergröße zuweist, als sie benötigen, und den ausgerichteten Zeiger als Anfang verwenden. Ich frage mich, sollte ich das in meinem Programm, ich habe keine Ahnung, gibt es keine Garantie der Ausrichtung der Adresse zurück von C++ 's neue Operation. Also schrieb ich ein kleines ProgrammGibt es eine Garantie der Ausrichtung der Adresse Rückkehr von C++ 's neuer Operation?

for(size_t i = 0; i < 100; ++i) { 
    char *p = new char[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
for(size_t i = 0; i < 100; ++i) { 
    short *p = new short[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
for(size_t i = 0; i < 100; ++i) { 
    float *p = new float[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
system("pause"); 

Der Compiler zu testen, die ich benutze ist Visual C++ Express 2008. Es scheint, dass alle Adressen der neuen Operation zurückgegeben ausgerichtet sind. Aber ich bin mir nicht sicher. Meine Frage ist also: Gibt es irgendeine Garantie? Wenn sie Garantie haben, muss ich mich nicht ausrichten, wenn nicht, muss ich.

Antwort

19

Die Ausrichtung hat die folgende Garantie des Standard (3.7.3.1/2):

Den Zeiger zurückgeführt wird in geeigneter Weise so ausgerichtet werden, dass es zu einem pointer jeden vollständigen Objekttypen umgewandelt werden kann und dann verwendet, um Zugriff auf das Objekt oder die Anordnung in dem Speicher zugeordnet (explizit durch einen Aufruf einer entsprechende Freigabe-Funktion, bis das Speicher Zuordnung aufgehoben wird).

EDIT: Dank timday zur Hervorhebung eines bug in gcc/glibc, wo die Garantie nicht gilt.

BEARBEITEN 2: Bens Kommentar hebt einen interessanten Randfall hervor. Die Anforderungen an die Zuweisungsroutinen sind nur für diejenigen vorgesehen, die vom Standard bereitgestellt werden. Wenn die Anwendung eine eigene Version hat, gibt es keine Garantie für das Ergebnis.

+0

In der Theorie. Wenn Sie gcc + glibc und SSE auf einem 32-Bit-System verwenden, beachten Sie in der Praxis http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15795. – timday

+0

@timay: Dieses Problem mit SSE-Typen galt auch für neuere Versionen von MSVC++ (z. B. .NET 2003). Ich habe nicht die neueste Version getestet, aber ich vermute, dass es immer noch so ist. –

+0

Der Wert, der von der 'operator new []()' -Helferzuweisungsfunktion zurückgegeben wird, die diese Anforderung betrifft, ist nicht derselbe Zeiger, den Sie vom Operator 'new []' erhalten. Diese Antwort ist falsch. –

4

Der neue/new [] -Operator der Plattform gibt Zeiger mit ausreichender Ausrichtung zurück, so dass sie gute Ergebnisse mit grundlegenden Datentypen (Double, Float, etc.) Zumindest sollte jeder vernünftige C++ - Compiler + Runtime das tun.

Wenn Sie spezielle Ausrichtung Anforderungen wie für SSE haben, dann ist es wahrscheinlich eine gute Idee, spezielle aligned_malloc Funktionen verwenden, oder rollen Sie Ihre eigenen.

4

ich auf einem System gearbeitet, wo sie die Ausrichtung verwendet, um die ungerade Bit für dort eigenen Gebrauch kostenlos!

Sie benutzten das ungerade Bit ein virtuelles Speichersystem zu implementieren.

Wenn ein Zeiger das ungerade Bit gesetzt hatte, benutzten sie, um anzuzeigen, dass es (minus dem ungeraden Bit) auf die Information zeigte, um die Daten von der Datenbank nicht die Daten selbst zu erhalten.

Ich dachte, das ist ein besonders fieses Bit der Codierung, die weit zu clever für sein eigenes gut war !!

Tony

+2

Sie werden getaggte Zeiger genannt, und sie sind nicht ungewöhnlich. Viele Programmiersprachenimplementierungen verwenden diesen Trick, um zwischen einem Zeiger und einer Ganzzahl zu unterscheiden. – geocar

+1

Und ARM Interworking verwendet es - wo zutreffend, ARM-Modus Code-Adressen sind gerade, Daumen-Modus-Adressen sind ungerade. Ich habe eine AVL-Baum-Implementierung gesehen, die die unteren zwei Bits verwendet hat, um den Höhenunterschied der Unterbäume eines Knotens zu speichern.Auf begrenzten Systemen stopfen Sie Flag-Bits, wo immer Sie können :-) –

+0

In den frühen Versionen von MAC OS (Classic) verwendeten sie die oberen 8 Bits für den Speichermanager. Die pTrs des 68000 hatten nur ein Maximum von 24 Bits, während die Adresse wo 32 Bits registriert. – AnthonyLambert

7

übrigens die MS documentation erwähnt etwas über malloc/neue Rückkehr-Adressen, die 16-Byte-ausgerichtet sind, sondern von Experimenten ist dies nicht der Fall ist. Ich brauchte zufällig die 16-Byte-Ausrichtung für ein Projekt (um Speicherkopien mit einem erweiterten Befehlssatz zu beschleunigen), am Ende habe ich meinen eigenen Zuordner geschrieben ...

+0

Ich fühle deinen Schmerz ... –

9

Dies ist eine späte Antwort, aber nur die Situation auf Linux zu klären - auf 64-Bit-Systemen Speicher ist immer 16 Byte ausgerichtet:

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

Die Adresse eines Blocks zurück durch malloc oder realloc im GNU-System ist immer ein Vielfaches von acht (oder sechzehn auf 64-Bit-Systemen).

Der new Operator ruft malloc intern so gilt dies für new auch (./gcc/libstdc++-v3/libsupc++/new_op.cc sehen).

Die Umsetzung malloc die grundsätzlich Teil der glibc ist definiert MALLOC_ALIGNMENT2*sizeof(size_t)size_t und sein 32-Bit ist = 4 Bytes und 64-Bit-= 8Byte auf einem x86-32 und x86-64 System, respectively.

$ cat ./glibc-2.14/malloc/malloc.c: 
... 
#ifndef INTERNAL_SIZE_T 
#define INTERNAL_SIZE_T size_t 
#endif 
... 
#define SIZE_SZ    (sizeof(INTERNAL_SIZE_T)) 
... 
#ifndef MALLOC_ALIGNMENT 
#define MALLOC_ALIGNMENT  (2 * SIZE_SZ) 
#endif