Ich habe meine Freizeit damit verbracht, ein paar Tests zu machen, lustige Dinge zu tun und einige verschiedene Dinge wie einfachen Algorithmus, Datenstruktur für meine persönliche Freude in C in diesen Tagen zu implementieren ....
Aber am Ende habe ich etwas interessantes für mich herausgefunden. Ich weiß nicht, warum dieses Ergebnis bis jetzt passiert ..
max_arr_count_index
wird abhängig vom arr[5]
-Wert zugewiesen, der hinter dem Ende des Arrays +1 liegt.
Gibt es jemanden, der mir das erklären kann? Ich weiß, dass es nicht sein sollte. Ich habe den Wert dem letzten Index des Arrays zugewiesen (hier, arr [5] = 30 im Problemfall) und es ist nicht sicher, und es ist undefiniertes Verhalten wie vom Standard definiert.
Ich werde nicht dasselbe im realen Feld machen, aber ich möchte hier nur mehr unter die Haube kommen.
LLVM und GCC haben mir das gleiche Ergebnis gegeben.
Code und Ergebnis ist unten:
[Kein Problemfall: Ich weise den Wert nach dem Ende des Index nicht zu]
%Vor%Kein Problemergebnis ist unten:
%Vor%[Problemfall: Ich habe den Wert nach dem Ende des Indexes zugewiesen]
%Vor%Ergebnis ist unten:
%Vor%Was den C-Standard anbelangt, ist dies natürlich undefiniertes Verhalten, und der Compiler könnte mach Fliegendämonen aus deiner Nase und es wäre gut - ish.
Aber Sie wollen tiefer gehen, wenn Sie "unter der Haube" fragen, also müssten wir im Wesentlichen nach der Assembler-Ausgabe suchen. Ein Auszug (erzeugt mit gcc -g test test.c
und objdump -S --disassemble test
) ist:
Wie Sie sehen, weiß der Disassembler bereits auf dieser Ebene, dass Sie effektiv max_arr_count_index
setzen. Aber warum?
Es liegt daran, dass das von GCC erzeugte Speicherlayout einfach so ist (und wir haben -g
mit gcc
verwendet, um Debug-Informationen einzubetten, damit der Disassembler wissen kann, welcher Speicherort welches Feld ist). Sie haben ein globales Array von fünf Ints und eine globale int-Variable, die direkt nacheinander deklariert werden. Die globale int-Variable ist einfach genau hinter dem Array im Speicher. Zugriff auf die ganze Zahl direkt hinter dem Ende des Arrays ergibt max_arr_count_index
.
Denken Sie daran, dass der Zugriff auf ein Element i
eines Arrays arr
von z. int
s ist (zumindest auf allen Architekturen I bekannt) einfach auf den Speicherort arr+sizeof(int)*i
zugreifen, wobei arr
die Adresse des ersten Elements ist.
Wie gesagt, dies ist ein undefiniertes Verhalten. GCC könnte auch die globale int-Variable vor dem Array anordnen, was zu verschiedenen Effekten führen würde, möglicherweise sogar das Programm beendet, wenn versucht wird, auf arr[5]
zuzugreifen, wenn es keine gültige Speicherseite an diesem Ort gibt.
Der Zugriff auf ein Array außerhalb der Grenzen ruft ein undefiniertes Verhalten auf. Nichts Gutes kann in diesem Fall erwartet werden. Die Größe von arr
ist 5
. Sie können auf arr
von arr[0]
bis arr[4]
zugreifen.
UB für einen Augenblick beiseite nehmen, die Erklärung für das Verhalten
%Vor% könnte die Variable max_arr_count_index
direkt nach dem Array arr
sein. Der Compiler kann den Speicher für max_arr_count_index
genau nach dem letzten Element des Arrays arr
zuweisen. Wenn beispielsweise arr[4]
auf 0x100
steht, wird Speicher für max_arr_count_index
auf 0x104
zugewiesen. Nach dem Array arr
ist die Adresse 0x104
. Da &arr[5]
die gleiche Adresse wie max_arr_count_index
ist, wenn Sie einen Wert arr[5]
zuweisen, schreiben Sie diesen Wert an die Adresse von max_arr_count_index
. Bitte beachten Sie, dass dies nicht genau passiert. Es ist eine Intuition für dieses Verhalten. Sobald es UB gibt, dann alle Wetten ab.
Das Beantworten von Fragen darüber, warum undefiniertes Verhalten auf eine bestimmte Art und Weise funktioniert, ist oxymoronisch und ein Rezept für das Downvoting. Aber auf der Grundlage habe ich bereits die Ruf-Kappe für heute getroffen und bin deswegen wesentlich entspannter, hier ist mein Schreiben:
arr[i]
wird als *(arr + i)
ausgewertet. Es ist offensichtlich, dass es zwischen arr
und max_arr_count_index
wahrscheinlich keine Auffüllung gibt, so dass arr + 5
wahrscheinlich gleich &max_arr_count_index
ist. Aber Zeigerarithmetik ist nur in Arrays gültig. Sie können einen Zeiger auf eins nach dem Ende des Arrays setzen, aber das Verhalten bei Dereferenzierung ist undefiniert .
Natürlich könnte der Compiler an einem anderen Tag Ihre Katze essen.