2014-11-15 10 views
6

ich einen Artikel über Verwendung von size_t und ptrdiff_t Datentypen here lesen, wenn ich auf dieses Beispiel kam:Unterschied in den Ergebnissen, wenn int und size_t mit

enter image description here

Der Code:

int A = -2; 
unsigned B = 1; 
int array[5] = { 1, 2, 3, 4, 5 }; 
int *ptr = array + 3; 
ptr = ptr + (A + B); //Error 
printf("%i\n", *ptr); 

Ich kann ein paar Dinge nicht verstehen. Erstens, wie kann das Hinzufügen einer signed und einer unsigned Nummer das Enter-Ergebnis in unsigned Typ? Wenn das Ergebnis in der Tat 0xFFFFFFFF von unsigned Typ ist, warum in einem 32-Bit-System, während es mit ptr, wird es als ptr-1 interpretiert werden, vorausgesetzt, dass die Zahl tatsächlich unsigned Typ und die führende 1 sollte nicht Zeichen bedeuten?

Zweitens, warum ist das Ergebnis im 64-Bit-System anders?

Kann mir bitte jemand erklären?

+3

Postleitzahl Hinzufügen, kein Bild von Code. – chux

+0

Siehe http://StackOverflow.com/Questions/24864103/Replacing-Arrays-Access-Variables-with-the-Right-Integer-Type – technosaurus

+0

Ich bin froh, dass jemand an meinem Artikel interessiert ist. Die Fragen haben bereits beantwortet. Ein wenig mehr Details über Adressarithmetik: http://www.viva64.com/en/l/0013/ Und im Allgemeinen eine Menge interessanter Dinge über die 64-Bit-Fehler: http://www.viva64.com/ en/l/full/ –

Antwort

4

1. Ich kann ein paar Dinge nicht verstehen. Erstens, wie kann das Hinzufügen einer vorzeichenbehafteten und einer vorzeichenlosen Zahl das Enter-Ergebnis in einen vorzeichenlosen Typ umwandeln?

Dies wird durch Integer-Promotions und Integer-Conversion-Rang definiert.

6.3.1.8 p1: Andernfalls, wenn der Operand, unsigned integer Typ hat hat Rang größer oder gleich dem Rang des Typs des anderen Operanden, dann wird der Operand mit vorzeichenbehaftete Ganzzahl-Typ wird auf die umgewandelte Typ des Operanden mit unsigned Integer-Typ.

In diesem Fall hat unsigned einen höheren Rang als int, daher wird int zu unsigned hochgestuft.

Die Umwandlung von int (-2) bis unsigned wird wie beschrieben durchgeführt:

6.3.1.3 p2: Andernfalls, wenn der neue Typ unsigned ist, wird der Wert durch wiederholte umgewandelte Hinzufügen oder Subtrahieren einer mehr als der Maximalwert, der in dem neuen Typ , bis der Wert dargestellt werden kann, ist im Bereich des neuen Typs

2. Wenn das Ergebnis in der Tat 0xFFFFFFFF von unsigned-Typ ist, warum in einem 32-Bit-System , während es mit ptr hinzugefügt wird, wird es interpretiert werden ed als ptr-1, vorausgesetzt, dass die Zahl tatsächlich vorzeichenloser Typ ist und die führende 1 nicht Zeichen bedeuten sollte?

Dies ist undefiniertes Verhalten und sollte nicht verlässlich sein, da C keinen arithmetischen Zeigerüberlauf definiert.

6.5.6 p8: Wenn sowohl der Zeiger Operand und das Ergebnis Punkt auf Elemente des gleichen Array-Objekt oder ein hinter dem letzten Element des Arrays Objekts die Auswertung darf nicht einen Überlauf erzeugen; Andernfalls ist das Verhalten undefiniert.

3. Zweitens, warum ist das Ergebnis im 64-Bit-System anders?

(Dies setzt voraus, (wie auch die Abbildung), die int und unsigned sind 4 Bytes.)

Das Ergebnis von A und B ist die gleiche wie in 1. beschrieben, dann wird dieses Ergebnis hinzugefügt der Zeiger. Da der Zeiger 8 Bytes ist und angenommen wird, dass die Addition nicht überläuft (es könnte immer noch, wenn ptr eine große Adresse hätte, die das gleiche undefinierte Verhalten wie in 2.) gibt, ist das Ergebnis eine Adresse.

Dies ist undefiniertes Verhalten, da der Zeiger weit außerhalb der Grenzen des Arrays zeigt.

1

Auf den meisten 64Bits sind int 32Bits, aber auf 32Bits-Systemen sind Zeiger auch 32Bits.

Daran erinnern, dass Arithmetik in 32bits - auf two's complement basierte Hardware, das Hinzufügen 0xFFFFFFFF fast gleich 1 ist, wie Subtrahieren: er überläuft und Sie erhalten die gleiche Anzahl minus 1 (es ist das gleiche Phänomen, wenn Sie 9 auf eine Zahl zwischen 0 hinzufügen und 9, erhalten Sie als Zahl minus 1 und ein tragen). Bei diesem Hardwaretyp ist die Codierung -1 tatsächlich derselbe Wert 0xFFFFFFFF, nur die Operation ist anders (vorzeichenbehaftet add versus unsigned add), und so wird im unsignierten Fall ein Übertrag erzeugt.

Auf 64 Bits sind Zeiger ... 64Bits. Das Hinzufügen eines 32-Bit-Werts zu einem 64-Bit-Wert erfordert, dass dieser 32-Bit-Wert auf 64 erweitert wird. unsigned Werte sind Null erweitert (dh die fehlenden Bits werden nur mit Nullen aufgefüllt), während vorzeichenbehaftete Werte vorzeichenerweitert sind Vorzeichen-Bit-Wert).

In diesem Fall wird das Hinzufügen eines nicht signierten Werts (der daher nicht vorzeichenerweitert wird) nicht überlaufen, was zu einem anderen Wert als das Original führt.

+0

Ich bin nie gut mit diesen Bit Manipulation Dinger. :(Also um klar zu sein, beim Hinzufügen, das Ergebnis ist die ursprüngliche Zahl -1, der Übertrag wird vernachlässigt, und damit die Nummer wird die ursprüngliche Zahl -1, ist das richtig? Warum tritt es nur auf 32-Bit-Systemen? – SexyBeast

+1

u64 + u32 kann immer noch überlaufen – 2501

+0

@ 2501 sollte jetzt klarer sein. – didierc

2

Die Operanden des Ausdrucks A + B unterliegen übliche arithmetische Umwandlung in C11 abgedeckt (n1570) p1 6.3.1.8:

[...]

Andernfalls die ganzzahligen aktionen [welches verlassen int und unsigned int unverändert] werden auf beiden Operanden durchgeführt. Dann werden die folgenden Regeln für die geförderten Operanden angewendet:

  • Wenn beide Operanden haben die gleiche Art [...]
  • Andernfalls, wenn beide Operanden Integer-Typen oder beide haben unsigned Integer-Typen unterzeichnet haben, [ ...]
  • Andernfalls, wenn der Operand, dessen Typ vorzeichenloser Integer hat, größer oder gleich dem Rang des Typs des anderen Operanden ist, wird der Operand mit vorzeichenbehafteten Integer-Typen in den Typ des unsignierten Operanden konvertiert ganzzahliger Typ
  • [...]

Die Typen int und unsigned int haben den gleichen Rang (ebd. 6.3.1.1 p1, 4 th bullet); Das Ergebnis der Addition hat den Typ unsigned int.

Auf 32-Bit-Systemen haben int und Zeiger normalerweise die gleiche Größe (32 Bit). Aus einer hardwaremäßigen Sicht (und unter der Annahme eines Zweierkomplements) ist das Subtrahieren von 1 und das Hinzufügen von -1u dasselbe (die Addition für vorzeichenbehaftete und unsignierte Typen ist gleich!), So dass der Zugriff auf das Array-Element zu funktionieren scheint.

Dies ist jedoch ein undefiniertes Verhalten, da array kein 0x100000003 rd Element enthält.

Auf 64-Bit, int hat in der Regel noch 32 Bit, aber Zeiger haben 64 Bit. Somit gibt es keine Umgehung und keine Äquivalenz zum Subtrahieren von 1 (von einem hardwarezentrischen Standpunkt aus ist das Verhalten in beiden Fällen nicht definiert).

Zur Veranschaulichung sagen ptr 0xabcdist, 0xffffffff Ausbeuten

abcd
+ ffffffff 

1abcd0122 
^-- The 1 is truncated for a 32-bit calculation, but not for 64-bit. 
Verwandte Themen