Warum wird diese Schleife bei einigen Plattformen und nicht bei anderen ausgeführt?
Ich habe vor kurzem angefangen C zu lernen und ich bin eine Klasse unter mit C als Subjekt. Ich bin derzeit rund um das Spiel mit loops und ich bin in einige merkwürdige Verhaltensweisen, die ich weiß nicht, wie zu erklären.
#include <stdio.h>
int main()
{
int array[10],i;
for (i = 0; i <=10 ; i++)
{
array[i]=0; /*code should never terminate*/
printf("test \n");
}
printf("%d \n", sizeof(array)/sizeof(int));
return 0;
}
Auf meinem laptop läuft Ubuntu 14.04, ist dieser code nicht zu brechen. Es läuft bis zur Fertigstellung. Auf meiner Schule computer mit CentOS 6.6, es läuft auch gut. Auf Windows 8.1, wird die Schleife nie beendet.
Was ist noch seltsamer ist, dass wenn ich edit den Zustand der for
Schleife: i <= 11
der code nur beendet, auf meinem laptop läuft Ubuntu. Es ist nie beendet in CentOS und Windows.
Kann mir jemand erklären, was passiert in der Erinnerung-und warum die verschiedenen Betriebssysteme läuft der gleiche code unterschiedliche Ergebnisse?
EDIT: ich kenne die for-Schleife geht out of bounds. Ich mache es absichtlich. Ich kann einfach nicht herausfinden, wie das Verhalten können unterschiedlich sein zwischen verschiedenen Betriebssystemen und Computern.
InformationsquelleAutor der Frage JonCav | 2015-06-24
Du musst angemeldet sein, um einen Kommentar abzugeben.
Du hast gerade entdeckt, Speicher stampfen. Sie können mehr darüber Lesen Sie hier: Was ist ein "memory stomp"?
Beim zuordnen von
int array[10],i;
diese Variablen gehen in den Speicher (speziell, Sie sind auf dem Stapel reserviert, das ist ein Speicherblock, der im Zusammenhang mit der Funktion).array[]
undi
sind wahrscheinlich nebeneinander im Speicher. Es scheint, dass auf Windows 8.1,i
befindet sich beiarray[10]
. Auf CentOS,i
befindet sich beiarray[11]
. Und auf Ubuntu, ist es in keiner Stelle (vielleicht ist es beiarray[-1]
?).Versuchen Sie, diese debugging-Anweisungen im code. Sollten Sie bemerken, dass nach iteration 10 oder 11,
array[i]
Punkte aufi
.InformationsquelleAutor der Antwort QuestionC
Der Fehler liegt zwischen diesen Stück code:
Seit
array
hat nur 10 Elemente, die in der letzten iterationarray[10] = 0;
ist ein buffer overflow. Buffer overflows sind UNDEFINIERTES VERHALTENwas bedeutet, dass Sie möglicherweise formatieren Sie Ihre Festplatte oder Dämonen führen zu Fliegen, die aus Ihrer Nase.Es ist ziemlich allgemein für alle stack-Variablen angelegt werden nebeneinander. Wenn
i
liegt, woarray[10]
schreibt, dann ist das UB wird zurückgesetzti
zu0
wodurch die nicht-Schleife.Zu beheben, ändern Sie die loop-Zustand zu
i < 10
.InformationsquelleAutor der Antwort o11c
Was sollte der Letzte Lauf der Schleife,schreiben Sie
array[10]
aber es gibt nur 10 Elemente im array, nummeriert von 0 bis 9. Die C-Sprache-Spezifikation sagt, dass dies "undefined behavior". Was dies in der Praxis bedeutet ist, dass ein Programm versucht zu schreiben, um dieint
-großer Teil des Speichers liegt, wird sofort nacharray
im Speicher. Was dann geschieht, hängt davon ab, was es tut, in der Tat, es liegen, und diese hängt nicht nur vom Betriebssystem, aber um so mehr auf den compiler, auf den compiler-Optionen (wie-Optimierungs-Einstellungen), auf der Prozessor-Architektur, die auf den umliegenden code, etc. Es könnte auch variieren von Ausführung zu Ausführung, z.B. durch address space randomization (wahrscheinlich nicht auf dieses Spielzeug Beispiel, aber es passiert im wirklichen Leben). Einige Möglichkeiten sind:i
. Die Schleife nie beendet, weili
startet bei 0.array
ist direkt am Ende einer Seite im virtuellen Speicher und die nächste Seite ist nicht zugeordnet.Was du beobachtet hast, auf Windows war, dass der compiler beschlossen, die variable
i
sofort nach dem array im Speicher, soarray[10] = 0
endete die Zuordnung zui
. Unter Ubuntu und CentOS, der compiler hat nicht Platzi
es. Fast alle C-Implementierungen der Gruppe lokale Variablen im Speicher, auf einem Speicher-stackmit einer großen Ausnahme: einige lokale Variablen gesetzt werden können, die vollständig in registriert. Auch wenn die variable auf den Stapel, die Reihenfolge der Variablen wird bestimmt durch den compiler, und es kann, hängt nicht nur von der Reihenfolge in der Quell-Datei, sondern auch auf deren Typen (zur Vermeidung der Verschwendung von Speicher-alignment-constraints, die Löcher), auf Ihren Namen, auf einige hash-Wert verwendet, in der eine compiler-interne Datenstruktur, etc.Wenn Sie möchten, um herauszufinden, was dein compiler entschieden haben, können Sie es sagen, zeigen Sie den assembler-code. Oh, und zu lernen, zu entziffern, assembler (es ist einfacher als es zu schreiben). Mit GCC (und einigen anderen Compilern, besonders in der Unix-Welt), übergeben Sie die option
-S
zu erzeugen assembler-code anstelle einer binären. Zum Beispiel, hier ist der assembler-Schnipsel für die Schleife aus kompilieren mit GCC auf amd64 mit der Optimierung option-O0
(keine Optimierung), mit Kommentaren, die manuell Hinzugefügt:Hier die variable
i
ist 52 bytes unterhalb der Spitze des Stapels, während der array beginnt 48 bytes unterhalb der Spitze des Stapels. So ist dieser compiler das zufällig platzierti
kurz vor dem array, man würde Sie überschreibeni
wenn Sie geschehen, zu schreiben, zuarray[-1]
. Wenn Sie ändernarray[i]=0
zuarray[9-i]=0
haben, erhalten Sie eine Endlosschleife auf dieser besonderen Plattform, die mit diesen speziellen compiler-Optionen.Lassen Sie uns nun kompilieren Sie Ihr Programm mit
gcc -O1
.Kürzeren! Der compiler hat nicht nur abgelehnt reservieren Sie einen Stapel Position für
i
— es ist immer nur in einem register gespeichertebx
— aber es ist noch nicht die Mühe gemacht zu allokieren von Speicher für diearray
oder um code zu generieren, um seine Elemente, weil es bemerkt, dass keines der Elemente immer verwendet.Um in diesem Beispiel mehr erzählen, lassen Sie uns sicherstellen, dass die array-Zuweisungen werden ausgeführt, indem der compiler mit etwas, das es nicht in der Lage ist, zu optimieren, entfernt. Eine einfache Möglichkeit dafür ist die Verwendung des array aus einer anderen Datei — aufgrund der separaten Zusammenstellung der compiler nicht weiß, was passiert, in einer anderen Datei (es sei denn, es optimiert zur link-Zeit, die
gcc -O0
odergcc -O1
nicht). Erstellen Sie eine Quelldateiuse_array.c
mitund ändern Sie Ihren source-code zu
Kompilieren mit
Dieser Zeit der assembler-code sieht wie folgt aus:
Nun das array auf dem stack, 44 bytes von der Spitze. Was
i
? Es scheint nicht überall! Aber der Schleifenzähler wird gehalten in das registerrbx
. Es ist nicht genaui
aber die Adresse desarray[i]
. Der compiler hat entschieden, dass, da der Wert deri
war nie direkt verwendet, es wurde kein Punkt in der Arithmetik zu berechnen, wo zu speichern 0 bei jedem Durchlauf der Schleife. Statt, die Adresse ist die loop-variable, und die Arithmetik für die Bestimmung der Grenzen erfolgte teilweise zur compile-Zeit (multiplizieren 11 Iterationen von 4 bytes pro array-element zu erhalten 44) und teilweise zur Laufzeit, aber für alle mal, bevor die Schleife gestartet wird (führen Sie eine Subtraktion um den ursprünglichen Wert).Selbst bei diesem sehr einfachen Beispiel haben wir gesehen, wie verändert der compiler-Optionen (einschalten-Optimierung) oder ändern etwas kleinere (
array[i]
zuarray[9-i]
) oder auch der Wechsel etwas scheinbar nicht (hinzufügen der Aufrufuse_array
) kann einen erheblichen Unterschied zu dem, was in dem ausführbaren Programm, der vom compiler erzeugt hat. Compiler-Optimierungen kann eine Menge Dinge tun, die angezeigt werden können unintuitiv, die an Programmen aufrufen zu undefiniertem Verhalten. Das ist, warum Undefiniertes Verhalten ist vollkommen undefiniert. Wenn Sie abweichen, immer so leicht von den Gleisen, in real-world-Programme, es kann sehr hart sein, zu verstehen, die Beziehung zwischen dem, was der code tut und was Sie getan haben sollte, sogar für erfahrene Programmierer.InformationsquelleAutor der Antwort Gilles
Im Gegensatz zu Java, C nicht-array-boundary-check, ich.e, es gibt keine
ArrayIndexOutOfBoundsException
den job machen Sie sicher, dass der array-index gültig ist, bleibt dem Programmierer. Tun dies auf Zweck führt zu undefiniertem Verhalten, alles könnte passieren.Für ein array:
Indizes sind nur gültig im Bereich
0
zu9
. Jedoch, Sie versuchen, zu:Zugang
array[10]
hier, ändern Sie die Bedingungi < 10
InformationsquelleAutor der Antwort Yu Hao
Haben Sie eine bounds violation, und auf die nicht-terminierende Plattformen, ich glaube, Sie sind versehentlich die Einstellung
i
auf null am Ende der Schleife, so dass es immer wieder startet.array[10]
ist ungültig; Sie enthält 10 Elemente, diearray[0]
durcharray[9]
undarray[10]
ist der 11. Die Schleife geschrieben werden sollen, zu stoppen vor10
wie folgt:Wo
array[10]
Länder wird durch die Implementierung festgelegt und lustig, auf beiden Plattformen ein, landet er aufi
die diese Plattformen offenbar lag direkt nacharray
.i
auf null gesetzt und die Schleife wiederholt sich immer. Für die anderen Plattformeni
kann sich vorarray
oderarray
können einige Polsterung nach.InformationsquelleAutor der Antwort Derek T. Jones
Erklären Sie
int array[10]
bedeutetarray
hat index0
zu9
(Gesamt10
integer-Elementen, die es halten kann). Aber die folgende Schleife,wird Schleife
0
zu10
bedeutet11
Zeit. Wenn alsoi = 10
es ein überlauf des Puffers und verursachen Undefiniertes Verhalten.Also versuchen Sie dies:
oder,
InformationsquelleAutor der Antwort rakeb.mazharul
Ist es undefiniert, bei
array[10]
und gibt Undefiniertes Verhaltenwie vorher beschrieben. Denken Sie daran, wie diese:Ich habe 10 Artikel in meinem Einkaufswagen. Sie sind:
0: Ein Feld von Getreide
1: Brot
2: Milch
3: Pie
4: Eier
5: Kuchen
6: Einen 2-liter-soda
7: Salat
8: Burger
9: Eis
cart[10]
ist nicht definiert und kann eine out of bounds exception in einigen Compilern. Aber viele scheinbar nicht. Die scheinbare 11th Element ist ein Element nicht tatsächlich in den Warenkorb gelegt. Die 11th Element verweist, was ich nenne, eine "poltergeist Element." Es nie gegeben, aber es war da.Warum einige Compiler geben
i
einen index vonarray[10]
oderarray[11]
oder sogararray[-1]
aufgrund Ihrer Initialisierung/Deklarationsanweisung. Manche Compiler interpretieren dies als:int
s fürarray[10]
und anderenint
block. um es einfacher zu machen, legte Sie direkt neben einander."array[10]
nicht aufi
.i
beiarray[-1]
(weil ein index eines Arrays kann nicht, oder sollte nicht negativ sein), oder weisen Sie es an einem völlig anderen Ort, weil das OS damit umgehen kann, und es ist sicherer.Einige Compiler wollen die Dinge gehen schneller, und einige Compiler lieber die Sicherheit. Es ist alles über Kontext. Wenn ich eine app zu entwickeln für die alte BREW-OS (die OS eine grundlegende Telefon), zum Beispiel, es wäre nicht die Sorge um die Sicherheit. Wenn ich die Entwicklung für ein iPhone 6 ist, dann könnte es schnell laufen, egal was, ich bräuchte also einen Schwerpunkt auf die Sicherheit. (Im ernst, haben Sie gelesen apples App Store-Richtlinien, oder Lesen, auf die Entwicklung von Swift und Swift 2.0?)
InformationsquelleAutor der Antwort DDPWNAGE
Seit Sie erstellt ein array der Größe 10 for-Schleife, Zustand sollte wie folgt sein:
Derzeit Sie sind versucht, den Zugriff auf die zugewiesene Position aus dem Speicher mit
array[10]
und verursacht es die Undefiniertes Verhalten. Undefiniertes Verhalten bedeutet, dass Ihr Programm sich Verhalten unbestimmt Mode, so lassen sich verschiedene Ausgänge in jeder Ausführung.InformationsquelleAutor der Antwort Steephen
Gut, C-compiler, die traditionell nicht überprüfen Sie für Grenzen. Sie können einen "segmentation fault" den Fall, Sie beziehen sich auf eine Lage, die nicht "dazugehören", um Ihren Prozess. Jedoch werden die lokalen Variablen zugewiesen werden, auf den stack und abhängig davon, wie der Speicher reserviert, der Bereich direkt hinter dem array (
array[10]
) gehört zu den Prozess-Speicher-segment. Also, keine " segmentation fault trap wird ausgelöst, und das ist das, was Sie scheinen zu erleben. Wie andere haben darauf hingewiesen, das ist Undefiniertes Verhalten in C und der code kann als unberechenbar. Da lernen Sie C, Sie sind besser dran, bekommen in der Gewohnheit der überprüfung für die Grenzen in Ihrem code.InformationsquelleAutor der Antwort unxnut
Darüber hinaus die Möglichkeit, dass der Speicher möglicherweise so ausgelegt, dass der Versuch, zu schreiben, zu
a[10]
eigentlich überschreibti
es wäre auch möglich, dass ein optimierender compiler könnte bestimmen, dass der loop-test nicht erreicht werden kann mit einem Wert voni
mehr als zehn ohne code mit ersten Zugriff auf das nicht existierende Arrayelementa[10]
.Da ein Versuch, den Zugriff auf dieses element wäre Undefiniertes Verhalten, würde der compiler keine Verpflichtungen in Bezug auf das, was das Programm machen könnte, nach diesem Punkt. Genauer gesagt, da würde der compiler keine Verpflichtung zum generieren von code zu überprüfen, den loop-index in jedem Fall, wo es möglicherweise größer als zehn, es würde keine Verpflichtung zum generieren von code, um es zu überprüfen; es könnte stattdessen annehmen, dass die
<=10
test immer Rendite stimmt. Beachten Sie, dass dies ist true, selbst wenn Sie den code Lesen würdea[10]
anstatt es zu schreiben.InformationsquelleAutor der Antwort supercat
Wenn Sie Durchlaufen Vergangenheit
i==9
weisen Sie null, um die "array-Elemente", die sich eigentlich Vergangenheit arrayso sind Sie overwritnig einige andere Daten. Wahrscheinlich überschreiben Sie diei
variable, die sich nacha[]
. So können Sie einfach zurücksetzen deri
variable auf null und so starten Sie den loop.Können Sie entdecken, dass Sie sich, wenn Sie gedruckt
i
in der Schleife:statt nur
Natürlich, das Ergebnis hängt sehr stark von der Zuweisung von Speicher für die Variablen, die wiederum abhängig von einem compiler und dessen Einstellungen, so ist es in der Regel Undefiniertes Verhalten — das ist der Grund, warum die Ergebnisse auf verschiedenen Rechnern oder verschiedenen Betriebssystemen oder auf unterschiedlichen Compilern unterscheiden können.
InformationsquelleAutor der Antwort CiaPan
der Fehler ist im Teil-array[10] w/c ist auch die Adresse von i (int array[10],i;).
wenn array[10] auf 0 eingestellt ist, dann wäre ich 0 w/c setzt die gesamte Schleife und
Ursachen der Endlosschleife.
es wird unendliche Schleife, wenn das array[10] ist zwischen 0-10.die richtige loop for (i = 0; i <10 ; i++) {...}
int array[10],i;
for (i = 0; i <=10 ; i++)
array[i]=0;
InformationsquelleAutor der Antwort Jonelle H. Castaneda
Werde ich vorschlagen, etwas, dass ich die Kraft finden über:
Versuchen zuweisen array[i] = 20;
Ich denke, das beendet werden soll, den code überall.. (Sie halten i< =10 oder ll)
Wenn diese ausgeführt wird, können Sie entscheiden, fest, dass die festgelegten Antworten hier sind schon richtig [die Antwort bezüglich des Speichers stampfen, eine für die ex.]
InformationsquelleAutor der Antwort Raining fire
Gibt es zwei Dinge, die hier falsch. Das int i ist eigentlich ein array-element array[10], wie man Sie auf den stack. Denn Sie erlaubt die Indizierung tatsächlich array[10] = 0, wird die loop (index, i, wird nie mehr als 10. Machen Sie es
for(i=0; i<10; i+=1)
.i++ ist, wie K&R es nennen würde, 'schlechten Stil'. Es ist die Inkrementierung von i durch die Größe von i, nicht 1. i++ ist für Zeiger Mathematik und i+=1 ist für algebra. Während dies hängt von der compiler, es ist nicht eine gute Konvention für die Portabilität.
InformationsquelleAutor der Antwort SkipBerne