
seg.server.fault
Ich weise Werte in einem C++-Programm außerhalb der Grenzen wie folgt zu:
#include <iostream>
using namespace std;
int main()
{
int array[2];
array[0] = 1;
array[1] = 2;
array[3] = 3;
array[4] = 4;
cout << array[3] << endl;
cout << array[4] << endl;
return 0;
}
Das Programm druckt 3
und 4
. Es sollte nicht möglich sein. Ich verwende g++ 4.3.3
Hier ist der Befehl zum Kompilieren und Ausführen
$ g++ -W -Wall errorRange.cpp -o errorRange
$ ./errorRange
3
4
Nur beim Zuweisen array[3000]=3000
gibt es mir einen Segmentierungsfehler.
Wenn gcc nicht nach Array-Grenzen sucht, wie kann ich dann sicher sein, dass mein Programm korrekt ist, da dies später zu ernsthaften Problemen führen kann?
Ich habe den obigen Code durch ersetzt
vector<int> vint(2);
vint[0] = 0;
vint[1] = 1;
vint[2] = 2;
vint[5] = 5;
cout << vint[2] << endl;
cout << vint[5] << endl;
und dieser erzeugt auch keinen Fehler.

jalf
Willkommen beim besten Freund eines jeden C/C++-Programmierers: Undefiniertes Verhalten.
Vieles ist aus verschiedenen Gründen nicht durch den Sprachstandard spezifiziert. Dies ist einer von ihnen.
Im Allgemeinen, wann immer Sie auf undefiniertes Verhalten stoßen, irgendetwas das könnte passieren. Die Anwendung kann abstürzen, einfrieren, Ihr CD-ROM-Laufwerk auswerfen oder Dämonen aus Ihrer Nase kommen lassen. Es kann Ihre Festplatte formatieren oder alle Ihre Pornos per E-Mail an Ihre Großmutter senden.
Es kann sogar sein, wenn Sie wirklich Pech haben, erscheinen richtig zu arbeiten.
Die Sprache sagt einfach, was passieren soll, wenn man auf die Elemente zugreift innerhalb die Grenzen eines Arrays. Es bleibt undefiniert, was passiert, wenn Sie die Grenzen verlassen. Es könnte erscheinen heute auf Ihrem Compiler funktionieren, aber es ist kein legales C oder C++, und es gibt keine Garantie dafür, dass es noch funktioniert, wenn Sie das Programm das nächste Mal ausführen. Oder dass es selbst jetzt noch keine wesentlichen Daten überschrieben hat und Sie einfach nicht auf die Probleme gestoßen sind, dass es ist wird verursachen – noch.
Wie für warum Es gibt keine Grenzenüberprüfung, es gibt ein paar Aspekte für die Antwort:
- Ein Array ist ein Überbleibsel von C. C-Arrays sind so primitiv, wie man nur bekommen kann. Nur eine Folge von Elementen mit zusammenhängenden Adressen. Es findet keine Begrenzungsprüfung statt, da lediglich Rohspeicher offengelegt wird. Die Implementierung eines robusten Bounding-Checking-Mechanismus wäre in C fast unmöglich gewesen.
- In C++ ist eine Begrenzungsprüfung für Klassentypen möglich. Aber ein Array ist immer noch das einfache alte C-kompatible. Es ist keine Klasse. Darüber hinaus basiert C++ auch auf einer anderen Regel, die die Überprüfung von Grenzen nicht ideal macht. Das Leitprinzip von C++ lautet: „Sie zahlen nicht für das, was Sie nicht nutzen“. Wenn Ihr Code korrekt ist, brauchen Sie keine Begrenzungsprüfung, und Sie sollten nicht gezwungen sein, für den Overhead der Laufzeitbegrenzungsprüfung zu zahlen.
- C++ bietet also die
std::vector
Klassenvorlage, die beides zulässt. operator[]
ist auf Effizienz ausgelegt. Der Sprachstandard verlangt nicht, dass er eine Begrenzungsprüfung durchführt (obwohl er dies auch nicht verbietet). Ein Vektor hat auch die at()
Mitgliedsfunktion welche ist garantiert Bounds-Check durchzuführen. In C++ erhalten Sie also das Beste aus beiden Welten, wenn Sie einen Vektor verwenden. Sie erhalten eine Array-ähnliche Leistung ohne Begrenzungsprüfung, und Sie erhalten die Möglichkeit, den grenzengeprüften Zugriff zu verwenden, wenn Sie dies wünschen.
Mit g++ können Sie die Befehlszeilenoption hinzufügen: -fstack-protector-all
.
Bei deinem Beispiel hat sich folgendes ergeben:
> g++ -o t -fstack-protector-all t.cc
> ./t
3
4
/bin/bash: line 1: 15450 Segmentation fault ./t
Es hilft Ihnen nicht wirklich, das Problem zu finden oder zu lösen, aber der segfault wird Sie zumindest darüber informieren etwas ist falsch.

Arkaitz Jimenez
g++ prüft nicht auf Array-Grenzen, und Sie überschreiben möglicherweise etwas mit 3,4, aber nichts wirklich Wichtiges, wenn Sie es mit höheren Zahlen versuchen, kommt es zu einem Absturz.
Sie überschreiben nur Teile des Stapels, die nicht verwendet werden, Sie könnten fortfahren, bis Sie das Ende des zugewiesenen Speicherplatzes für den Stapel erreichen, und es würde schließlich abstürzen
BEARBEITEN: Sie haben keine Möglichkeit, damit umzugehen, vielleicht könnte ein statischer Codeanalysator diese Fehler aufdecken, aber das ist zu einfach, Sie haben möglicherweise ähnliche (aber komplexere) Fehler, die selbst für statische Analysatoren unentdeckt bleiben

jkeys
Es ist ein undefiniertes Verhalten, soweit ich weiß. Führen Sie ein größeres Programm damit aus und es wird irgendwo auf dem Weg abstürzen. Die Überprüfung von Grenzen ist kein Teil von Raw-Arrays (oder sogar std::vector).
Verwenden Sie std::vector mit std::vector::iterator
‘s stattdessen, damit Sie sich keine Sorgen machen müssen.
Bearbeiten:
Führen Sie dies nur zum Spaß aus und sehen Sie, wie lange es dauert, bis Sie abstürzen:
int main()
{
int arr[1];
for (int i = 0; i != 100000; i++)
{
arr[i] = i;
}
return 0; //will be lucky to ever reach this
}
Edit2:
Führen Sie das nicht aus.
Edit3:
OK, hier ist eine kurze Lektion über Arrays und ihre Beziehungen zu Zeigern:
Wenn Sie die Array-Indizierung verwenden, verwenden Sie in Wirklichkeit einen getarnten Zeiger (genannt “Referenz”), der automatisch dereferenziert wird. Deshalb statt *(array+1) array[1] gibt automatisch den Wert an diesem Index zurück.
Wenn Sie einen Zeiger auf ein Array haben, wie folgt:
int arr[5];
int *ptr = arr;
Dann zerfällt das “Array” in der zweiten Deklaration wirklich zu einem Zeiger auf das erste Array. Dies ist ein äquivalentes Verhalten zu diesem:
int *ptr = &arr[0];
Wenn Sie versuchen, über das hinaus zuzugreifen, was Sie zugewiesen haben, verwenden Sie wirklich nur einen Zeiger auf anderen Speicher (worüber sich C++ nicht beschweren wird). Wenn ich mein obiges Beispielprogramm nehme, ist das äquivalent zu diesem:
int main()
{
int arr[1];
int *ptr = arr;
for (int i = 0; i != 100000; i++, ptr++)
{
*ptr++ = i;
}
return 0; //will be lucky to ever reach this
}
Der Compiler wird sich nicht beschweren, denn beim Programmieren muss man oft mit anderen Programmen, insbesondere dem Betriebssystem, kommunizieren. Dies geschieht ziemlich oft mit Zeigern.
Hinweis
Wenn Sie schnelle Constraint-Size-Arrays mit Bereichsfehlerprüfung haben möchten, versuchen Sie es mit boost::array(Auch std::tr1::array von <tr1/array>
es wird in der nächsten C++-Spezifikation ein Standardcontainer sein). Es ist viel schneller als std::vector. Es reserviert Speicher auf dem Heap oder innerhalb der Klasseninstanz, genau wie int array[].
Dies ist ein einfacher Beispielcode:
#include <iostream>
#include <boost/array.hpp>
int main()
{
boost::array<int,2> array;
array.at(0) = 1; // checking index is inside range
array[1] = 2; // no error check, as fast as int array[2];
try
{
// index is inside range
std::cout << "array.at(0) = " << array.at(0) << std::endl;
// index is outside range, throwing exception
std::cout << "array.at(2) = " << array.at(2) << std::endl;
// never comes here
std::cout << "array.at(1) = " << array.at(1) << std::endl;
}
catch(const std::out_of_range& r)
{
std::cout << "Something goes wrong: " << r.what() << std::endl;
}
return 0;
}
Dieses Programm druckt:
array.at(0) = 1
Something goes wrong: array<>: index out of range
C oder C++ prüfen die Grenzen eines Array-Zugriffs nicht.
Sie weisen das Array auf dem Stack zu. Indizieren des Arrays über array[3]
ist äquivalent zu *(array + 3)
wobei array ein Zeiger auf &array ist[0]. Dies führt zu undefiniertem Verhalten.
Eine Möglichkeit, dies zu fangen manchmal in C ist die Verwendung eines statischen Prüfers, wie z Schiene. Wenn du läufst:
splint +bounds array.c
an,
int main(void)
{
int array[1];
array[1] = 1;
return 0;
}
dann bekommst du die warnung:
array.c: (in Hauptfunktion) array.c:5:9: Wahrscheinlich außerhalb des Bereichs speichern: array[1]
Einschränkung kann nicht aufgelöst werden: Erfordert 0 >= 1 Erforderlich, um die Vorbedingung zu erfüllen: Erfordert maxSet(array @ array.c:5:9) >= 1 Ein Speicherschreibvorgang kann an eine Adresse jenseits des zugewiesenen Puffers schreiben.
Führen Sie das durch Valgrind und möglicherweise wird ein Fehler angezeigt.
Wie Falaina betonte, erkennt Valgrind nicht viele Fälle von Stack-Korruption. Ich habe das Beispiel gerade unter valgrind ausprobiert, und es meldet tatsächlich null Fehler. Valgrind kann jedoch beim Auffinden vieler anderer Arten von Speicherproblemen hilfreich sein, es ist in diesem Fall nur nicht besonders nützlich, es sei denn, Sie ändern Ihr bulid so, dass es die Option –stack-check enthält. Wenn Sie das Beispiel erstellen und ausführen als
g++ --stack-check -W -Wall errorRange.cpp -o errorRange
valgrind ./errorRange
Valgrind Wille einen Fehler melden.
10034100cookie-checkDer Zugriff auf ein Array außerhalb der Grenzen gibt keinen Fehler, warum?yes
Verwandte Frage: stackoverflow.com/questions/671703/…
– TSomKes
6. August 2009 um 16:15 Uhr
Der Code ist natürlich fehlerhaft, aber er generiert nicht definiert Verhalten. Undefiniert bedeutet, dass es vollständig ausgeführt werden kann oder nicht. Es gibt keine Garantie für einen Absturz.
– dmckee — Ex-Moderator-Kätzchen
6. August 2009 um 16:17 Uhr
Sie können sicher sein, dass Ihr Programm korrekt ist, indem Sie nicht mit rohen Arrays herumspielen. C++-Programmierer sollten stattdessen Containerklassen verwenden, außer bei der Embedded/OS-Programmierung. Lesen Sie dies aus Gründen zu Benutzercontainern. parashift.com/c++-faq-lite/containers.html
– jkeys
6. August 2009 um 16:25 Uhr
Denken Sie daran, dass Vektoren nicht unbedingt eine Reichweitenprüfung verwenden []. Die Verwendung von .at() macht dasselbe wie [] aber Reichweite-Check.
– David Thornley
6. August 2009 um 16:31 Uhr
EIN
vector
nicht Automatische Größenänderung beim Zugriff auf Out-of-Bounds-Elemente! Es ist nur UB!– Pavel Minaev
6. August 2009 um 17:02 Uhr