2013-02-08 18 views
5

ist die folgende wohldefinierte:Casting zwischen primitiven Typ Zeiger

char* charPtr = new char[42]; 
int* intPtr = (int*)charPtr; 

charPtr++; 
intPtr = (int*) charPtr; 

Die intPtr nicht richtig ausgerichtet ist (in zumindest einer der beiden Fälle). Ist es illegal, es einfach dort zu haben? Benutzt UB sie zu irgendeinem Zeitpunkt? Wie kannst du es benutzen und wie kannst du nicht?

+0

Unausgeglichener Zugriff würde passieren (auf relevanten Prozessoren), wenn Sie danach versuchen, 'intPtr' zu verwenden. Also wäre es definitiv nicht akzeptabel. Obwohl es bei einigen Prozessoren funktionieren kann - z. x86 wird glücklich, wenn ein wenig langsamer, unausgerichteten Speicher lesen. –

+0

Ich denke, du kannst zurückwerfen, aber nach der Besetzung nicht mehr verwenden :) Das müsste ich allerdings überprüfen. –

+0

Was meinst du mit "Kannst du es zurückwerfen?" –

Antwort

1

Zunächst natürlich: der Zeiger wird garantiert in den ersten Fall ausgerichtet (von §5.3.4/10 und §3.7.4.1/2), und kann korrekt in beiden Fällen ausgerichtet werden. (Natürlich, wenn sizeof(int) == 1, aber , auch wenn dies nicht der Fall ist, hat eine Implementierung nicht notwendigerweise Ausrichtungsanforderungen haben.)

Und die Dinge klar zu machen: Ihre Abgüsse alle reinterpret_cast sind.

Darüber hinaus ist dies eine interessante Frage, denn bis kann ich sagen, gibt es keinen Unterschied in den beiden Casts, soweit der Standard betroffen ist. Die Ergebnisse der Umwandlung sind nicht spezifiziert (gemäß §5.2.10/7); Sie sind nicht einmal garantiert , dass die Umwandlung in eine char* wird in den ursprünglichen Wert resultieren.(Es wird offensichtlich nicht zum Beispiel an Maschinen wo int* kleiner als ein char*.)

In der Praxis natürlich: Der Standard verlangt, dass die Rückkehr Wert von new char[N] ausreichend für einen beliebigen Wert ausgerichtet werden die kann hineinpassen, so dass Sie garantiert sind in der Lage sein, zu tun:

intPtr = new (charPtr) int; 

, die genau die gleiche Wirkung wie Ihr Stich hat angegeben, dass die Standardkonstruktors für int ist ein no-op. (Und unter der Annahme, dass sizeof(int) <= 42.) So ist es schwer, eine Implementierung vorstellen, in der der erste Teil fehlschlägt. Sie sollten in der Lage sein, die intPtr genau wie alle anderen legal erhaltenen intPtr zu verwenden. Und die Idee, dass die Umwandlung zurück in eine char* würde irgendwie in einem anderen Wert von der ursprünglichen char* scheint absurd ergeben.

Im zweiten Teil sind alle Wetten ab: Sie kann auf jeden Fall nicht dereferenzieren den Zeiger (es sei denn, Ihre Implementierung sonst garantiert), und es ist auch durchaus möglich, dass es wieder zu char* Ergebnisse in etwas anderes zu konvertieren. (Stellen Sie sich ein Wort Maschine gerichtet, zum Beispiel, in dem bis eine char* auf einen int* Runden zu konvertieren. Dann Umwandlung in würde wieder ein char* die sizeof(int) höher als das Original war. Oder wo ein Versuch, einen falsch ausgerichteten Zeiger konvertieren immer Ergebnis in einem Null-Zeiger.)

3

Im Allgemeinen ist das Ergebnis nicht spezifiziert (5.2.10p7), wenn die Ausrichtungsanforderungen von int größer sind als die von char, die sie in der Regel sein. Das Ergebnis wird ein gültiger Wert vom Typ int * sein, so dass es z.B. gedruckt als Zeiger mit operator<< oder konvertiert zu intptr_t.

Da das Ergebnis eine nicht spezifizierte Wert hat, es sei denn durch die Implementierung spezifiziert ist es undefinierte Verhalten zu indirekten und führen es L-Wert-zu-R-Wert-Umwandlung des resultierenden int lvalue (außer in unevaluierten Kontexten). In char * zurückkonvertieren wird nicht unbedingt hin und zurück.

Wenn jedoch das Original char * selbst das Ergebnis einer Besetzung aus int * war, dann gilt die Besetzung int * als die zweite Hälfte einer Rundreise; In diesem Fall ist die Besetzung definiert.

Insbesondere in dem Fall, in dem die oben char * das Ergebnis eines new[] Ausdruck war, sind wir (5.3.4p10), dass der Zeiger char * garantiert angemessen für int ausgerichtet ist, so lange wie sizeof(int) <= 42. Da der Ausdruck new[] seinen Speicher von einer Zuordnungsfunktion bezieht, gilt 3.7.4.1p2; Der void * Zeiger kann zu einem Zeiger eines vollständigen Objekttyps mit einer fundamentalen Ausrichtungsanforderung konvertiert werden und dann verwendet werden, um auf das [...] Objekt zuzugreifen, das zusammen mit der Anmerkung zu 5.3.4p10 stark dasselbe besagt gilt für den char * Zeiger, der durch den Ausdruck new[] zurückgegeben wird. In diesem Fall ist int * ein Zeiger auf ein nicht initialisiertes int-Objekt. Daher ist die Ausführung der lvalue-to-rvalue-Konvertierung für ihre Indirektion nicht definiert (3.8p6), aber die Zuweisung zu ihrer Indirektion ist vollständig definiert. Das int Objekt ist in dem Speicher zugewiesen (3.7.4.1p2) so Konvertieren der int * zurück zu char * wird den ursprünglichen Wert pro 1.8p6 ergeben. Dies gilt nicht für den inkrementierten Zeiger char *, es sei denn, sizeof(int) == 1 ist es nicht die Adresse eines Objekts int.

+0

Das ursprüngliche 'char *' war das Ergebnis von 'new char [42]'. –