Warum kann ich in C einen Wert sehen, der über das Ende eines Arrays in einer anderen Variablen geschrieben wurde?

7

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%     
boraseoksoon 04.11.2016, 16:07
quelle

4 Antworten

7

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:

%Vor%

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.

    
Jonas Wielicki 04.11.2016, 16:18
quelle
7

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.

    
haccks 04.11.2016 16:10
quelle
3

Sie haben max_arr_count_index nach arr deklariert, also liegt max_arr_count_index wahrscheinlich in der nächsten Adresse relativ zu arr , also ist eine Zuweisung zu arr+5 eine Zuweisung zu max_arr_count_index address.

    
Gabriel 04.11.2016 16:15
quelle
3

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.

    
Bathsheba 04.11.2016 16:11
quelle

Tags und Links