Was ist der Zweck der Zuweisung einer bestimmten Speichermenge für Arrays in C++?
Lesezeit: 7 Minuten
Ich bin ein Student, der dieses Semester einen Kurs über Datenstrukturen in C++ belegt, und ich bin auf etwas gestoßen, das ich heute Abend nicht ganz verstehe. Angenommen, ich würde einen Zeiger auf ein Array auf dem Heap erstellen:
int* arrayPtr = new int [4];
Ich kann auf dieses Array mit Zeigersyntax zugreifen
int value = *(arrayPtr + index);
Aber wenn ich der Speicherposition unmittelbar nach dem Ende des für das Array zugewiesenen Speicherplatzes einen weiteren Wert hinzufügen würde, könnte ich darauf zugreifen
*(arrayPtr + 4) = 0;
int nextPos = *(arrayPtr + 4);
//the value of nextPos will be 0, or whatever value I previously filled that space with
Die Speicherposition von *(arrayPtr + 4) liegt hinter dem Ende des für das Array zugewiesenen Speicherplatzes. Aber soweit ich verstehe, würde das obige immer noch keine Probleme verursachen. Abgesehen davon, dass es eine Anforderung von C++ ist, warum sollte man Arrays überhaupt eine bestimmte Größe geben, wenn man sie deklariert?
“Das obige würde immer noch keine Probleme verursachen” Warum nicht? Wenn sich in dieser Speicherregion noch etwas anderes befindet, könnten Sie in große Schwierigkeiten geraten …
– Haraldkl
1. Oktober 15 um 4:00 Uhr
Diese Dinge führen zu dem, was als “undefinierter Fehler” bekannt ist. Sie funktionieren manchmal, aber der Haken ist, dass die Wahrscheinlichkeit, dass die Anwendung zu einem unvorhergesehenen Zeitpunkt abstürzt, sehr hoch ist … Segmentierungsfehler usw.
– Basav
1. Oktober 15 um 4:04 Uhr
Wenn Sie einen Gartenschuppen bauen, der das Grundstück Ihres Nachbarn überragt, bemerkt er das vielleicht zunächst nicht, aber er wird ziemlich wütend sein, wenn er herausfindet, dass Sie seine Petunien zerdrückt haben.
– Benutzer657267
1. Oktober 15 um 4:05 Uhr
Wenn Sie das Ende des zugewiesenen Speichers überschreiten, greifen Sie tatsächlich auf den Speicher eines anderen Objekts zu (oder Speicher, der gerade frei ist, sich aber später ändern könnte). So dass es Wille dir Probleme bereiten. Vor allem, wenn Sie versuchen, etwas dazu zu schreiben.
Okay, ich denke, ich habe nicht an die Möglichkeit gedacht, dass es überschrieben wird. Ich bin erst seit ein paar Wochen in diesem Kurs und habe noch nie wirklich viel über Speicherverwaltung nachgedacht, also bin ich immer noch ziemlich neu in diesem Zeug. Danke fürs klarstellen
– daviddorsey4
1. Oktober 15 um 4:18 Uhr
John3136
Ich kann auf dieses Array mit Zeigersyntax zugreifen
int value = *(arrayPtr + index);
Ja, aber nicht. Benutzen arrayPtr[index]
Die Speicherposition von *(arrayPtr + 4) liegt hinter dem Ende des für das Array zugewiesenen Speicherplatzes. Aber soweit ich verstehe, würde das obige immer noch keine Probleme verursachen.
Du verstehst falsch. Oh, so falsch. Sie rufen undefiniertes Verhalten auf und undefiniertes Verhalten ist undefiniert. Es kann eine Woche lang funktionieren, dann an einem Tag nächste Woche kaputt gehen und Sie werden sich fragen, warum. Wenn Sie die Sammlungsgröße nicht im Voraus kennen, verwenden Sie etwas Dynamisches wie a vector anstelle eines Arrays.
Ja, in C/C++ können Sie auf Speicher außerhalb des angeblich zugewiesenen Speicherplatzes zugreifen. Manchmal. Dies wird als bezeichnet undefiniertes Verhalten.
Im Grunde haben Sie dem Compiler und dem Speicherverwaltungssystem mitgeteilt, dass Sie Speicherplatz zum Speichern von vier Ganzzahlen benötigen, und das Speicherverwaltungssystem hat Ihnen Speicherplatz zum Speichern von vier Ganzzahlen zugewiesen. Es gab Ihnen einen Hinweis auf diesen Raum. In der internen Abrechnung des Speichermanagers sind diese RAM-Bytes nun belegt, bis Sie aufrufen delete[] arrayPtr;.
Der Speichermanager hat das nächste Byte jedoch nicht für Sie zugewiesen. Sie haben im Allgemeinen keine Möglichkeit zu wissen, was das nächste Byte ist oder wem es gehört.
In einem einfachen Beispielprogramm wie Ihrem Beispiel, das nur ein paar Bytes zuweist und nichts anderes zuweist, besteht die Möglichkeit, dass das nächste Byte zu Ihrem Programm gehört und nicht belegt ist. Wenn dieses Array der einzige dynamisch zugewiesene Speicher in Ihrem Programm ist, dann ist es das wahrscheinlich, könnte sein sicher über das Ende laufen.
Aber in einem komplexeren Programm mit mehreren dynamischen Speicherzuweisungen und -freigaben, insbesondere in der Nähe der Ränder von Speicherseiten, haben Sie wirklich keine gute Möglichkeit zu wissen, welche Bytes außerhalb des angeforderten Speichers enthalten sind. Wenn Sie also in Bytes außerhalb des angeforderten Speichers schreiben new Sie könnten im Grunde alles schreiben.
Das ist wo undefiniertes Verhalten hereinkommt. Da Sie nicht wissen, was sich in dem Bereich befindet, in den Sie geschrieben haben, wissen Sie nicht, was als Ergebnis passieren wird. Hier sind einige Beispiele für Dinge, die passieren könnten:
Der Speicher wurde beim Schreiben nicht zugewiesen. In diesem Fall sind die Daten in Ordnung, und es scheint nichts Schlimmes zu passieren. Wenn jedoch eine spätere Speicherzuordnung diesen Speicherplatz verwendet, geht alles verloren, was Sie dort abzulegen versucht haben.
Der Speicher wurde zugewiesen, als Sie darauf geschrieben haben. In diesem Fall, herzlichen Glückwunsch, Sie haben einfach einige zufällige Bytes aus einer anderen Datenstruktur irgendwo anders in Ihrem Programm überschrieben. Stellen Sie sich vor, Sie ersetzen irgendwo in einem Ihrer Objekte eine Variable durch zufällige Daten, und überlegen Sie, was das für Ihr Programm bedeuten würde. Vielleicht hat eine Liste woanders jetzt die falsche Zählung. Vielleicht hat eine Zeichenfolge jetzt einige zufällige Werte für die ersten paar Zeichen oder ist jetzt leer, weil Sie diese Zeichen durch Nullen ersetzt haben.
Das Array wurde am Rand einer Seite zugewiesen, sodass die nächsten Bytes nicht zu Ihrem Programm gehören. Die Adresse liegt außerhalb der Zuordnung Ihres Programms. In diesem Fall erkennt das Betriebssystem, dass Sie auf zufälligen Speicher zugreifen, der nicht Ihnen gehört, und beendet Ihr Programm sofort mit SIGSEGV.
Grundsätzlich, undefiniertes Verhalten bedeutet, dass Sie etwas Illegales tun, aber weil C/C++ darauf ausgelegt ist, schnell zu sein, enthalten die Sprachdesigner keine explizite Überprüfung, um sicherzustellen, dass Sie nicht gegen die Regeln verstoßen, wie andere Sprachen (z. B. Java, C#). Sie listen einfach das Verhalten beim Verstoß gegen die Regeln als undefiniert auf, und dann können die Leute, die die Compiler erstellen, einen einfacheren, schnelleren Code als Ausgabe haben, da keine Überprüfungen der Array-Grenzen durchgeführt werden, und wenn Sie die Regeln brechen, ist es Ihr eigenes Problem.
Also ja, das funktioniert manchmal, aber verlassen Sie sich nie darauf.
In einer rein abstrakten Umgebung, in der Sie sich nur darum kümmern müssen, ob die Logik des Algorithmus stimmt, würde dies keine Probleme verursachen. In diesem Fall gibt es überhaupt keinen Grund, die Größe eines Arrays anzugeben. Ihr Computer existiert jedoch in der physischen Welt und hat nur eine begrenzte Menge an Arbeitsspeicher. Wenn Sie Speicher zuweisen, bitten Sie das Betriebssystem, Ihnen die Nutzung eines Teils des endlichen Speichers des Computers zu gestatten. Wenn Sie darüber hinausgehen, das Betriebssystem sollen stoppen Sie, normalerweise indem Sie Ihren Prozess / Ihr Programm beenden.
Ja, Sie müssen es als arrayptr schreiben[index] weil die Position im Speicher von * (arrayptr + 4) hinter dem Ende des Platzes liegt, den Sie für das Array zugewiesen haben. Es ist der Fehler in C++, dass die Array-Größe nach der Zuweisung nicht erweitert werden kann.
“Es ist der Fehler in C++, dass die Array-Größe nach der Zuweisung nicht erweitert werden kann.” – Unsinn … es ist leicht in C++, um einen Array-ähnlichen Container zu erstellen, der sich automatisch auf die tatsächlich verwendeten Indizes “erweitert”, da C++ das Überladen von unterstützt operator[], und – etwas weniger transparent, aber auch weniger fehleranfällig – vector::push_back und ::emplace Unterstützen Sie so etwas, während reserve und resize sind expliziter. ::at ermöglicht kontrollierten Zugriff. Das ist ziemlich umfassend. Und das alles hat nichts mit der Wahl zu tun arrayptr[index] vs. *(arrayptr + 4) ohnehin.
– Toni Delroy
1. Oktober 15 um 4:50 Uhr
“Es ist der Fehler in C++, dass die Array-Größe nach der Zuweisung nicht erweitert werden kann.” – Unsinn … es ist leicht in C++, um einen Array-ähnlichen Container zu erstellen, der sich automatisch auf die tatsächlich verwendeten Indizes “erweitert”, da C++ das Überladen von unterstützt operator[], und – etwas weniger transparent, aber auch weniger fehleranfällig – vector::push_back und ::emplace Unterstützen Sie so etwas, während reserve und resize sind expliziter. ::at ermöglicht kontrollierten Zugriff. Das ist ziemlich umfassend. Und das alles hat nichts mit der Wahl zu tun arrayptr[index] vs. *(arrayptr + 4) ohnehin.
– Toni Delroy
1. Oktober 15 um 4:50 Uhr
.
8112900cookie-checkWas ist der Zweck der Zuweisung einer bestimmten Speichermenge für Arrays in C++?yes
“Das obige würde immer noch keine Probleme verursachen” Warum nicht? Wenn sich in dieser Speicherregion noch etwas anderes befindet, könnten Sie in große Schwierigkeiten geraten …
– Haraldkl
1. Oktober 15 um 4:00 Uhr
Diese Dinge führen zu dem, was als “undefinierter Fehler” bekannt ist. Sie funktionieren manchmal, aber der Haken ist, dass die Wahrscheinlichkeit, dass die Anwendung zu einem unvorhergesehenen Zeitpunkt abstürzt, sehr hoch ist … Segmentierungsfehler usw.
– Basav
1. Oktober 15 um 4:04 Uhr
Wenn Sie einen Gartenschuppen bauen, der das Grundstück Ihres Nachbarn überragt, bemerkt er das vielleicht zunächst nicht, aber er wird ziemlich wütend sein, wenn er herausfindet, dass Sie seine Petunien zerdrückt haben.
– Benutzer657267
1. Oktober 15 um 4:05 Uhr