Der Zugriff auf ein Array außerhalb der Grenzen gibt keinen Fehler, warum?

Lesezeit: 11 Minuten

Der Zugriff auf ein Array auserhalb der Grenzen gibt keinen
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.

  • 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

1647300616 156 Der Zugriff auf ein Array auserhalb der Grenzen gibt keinen
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.

  • @Jaif: Wir verwenden dieses Array-Ding schon so lange, aber warum gibt es immer noch keinen Test, um einen solchen einfachen Fehler zu überprüfen?

    – seg.server.fehler

    6. August 2009 um 16:40 Uhr

  • Das Designprinzip von C++ war, dass es nicht langsamer sein sollte als der äquivalente C-Code, und dass C keine Array-Bound-Prüfung durchführt. Das C-Designprinzip war im Grunde Geschwindigkeit, da es auf die Systemprogrammierung abzielte. Die Überprüfung der Array-Grenze benötigt Zeit und wird daher nicht durchgeführt. Für die meisten Anwendungen in C++ sollten Sie ohnehin einen Container anstelle eines Arrays verwenden, und Sie können zwischen gebundener Prüfung oder keiner gebundenen Prüfung wählen, indem Sie entweder über .at() oder auf ein Element zugreifen [] bzw.

    – KTC

    6. August 2009 um 16:47 Uhr

  • @seg So ein Check kostet was. Wenn Sie den richtigen Code schreiben, möchten Sie diesen Preis nicht zahlen. Abgesehen davon bin ich zu einem vollständigen Konvertierer für die at()-Methode von std::vector geworden, die überprüft wird. Die Verwendung hat einige Fehler in dem, was ich für “richtigen” Code hielt, aufgedeckt.

    anon

    6. August 2009 um 16:48 Uhr

  • Ich glaube, dass alte Versionen von GCC tatsächlich Emacs und eine Simulation von Towers of Hanoi darin gestartet haben, als es auf bestimmte Arten von undefiniertem Verhalten stieß. Wie ich sagte, irgendetwas könnte passieren. 😉

    – jalf

    6. August 2009 um 17:18 Uhr

  • Es wurde bereits alles gesagt, daher rechtfertigt dies nur eine kleine Ergänzung. Debug-Builds können unter diesen Umständen im Vergleich zu Release-Builds sehr nachsichtig sein. Da Debug-Informationen in Debug-Binärdateien enthalten sind, ist die Wahrscheinlichkeit geringer, dass etwas Wichtiges überschrieben wird. Das ist manchmal der Grund, warum die Debug-Builds gut zu funktionieren scheinen, während der Release-Build abstürzt.

    – Reich

    6. August 2009 um 18:02 Uhr

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.

  • Ich habe gerade eine noch bessere Option gefunden: -Schmutzfänger

    – Hallo Engel

    24. Dezember 2014 um 8:00 Uhr

  • @Hi-Angel: Modernes Äquivalent ist -fsanitize=address die diesen Fehler sowohl zur Kompilierzeit (bei Optimierung) als auch zur Laufzeit abfängt.

    – Nate Eldredge

    15. Juli 2020 um 14:10 Uhr


  • @NateEldredge +1, heutzutage benutze ich sogar -fsanitize=undefined,address. Aber es ist erwähnenswert, dass es dort sind seltene Eckfälle mit der Standardbibliothek, wenn der Zugriff außerhalb der Grenzen vom Desinfektionsmittel nicht erkannt wird. Aus diesem Grund würde ich empfehlen, zusätzlich zu verwenden -D_GLIBCXX_DEBUG Option, die noch mehr Überprüfungen hinzufügt.

    – Hallo Engel

    15. Juli 2020 um 15:23 Uhr

  • Danke Hi Angel. wann -fmudflap und -fsanitize=address hat bei mir nicht funktioniert, -fsanitize=undefined,address fand nicht nur eine Funktion, die keinen Wert zurückgab, sondern auch die Array-Zuweisung, die außerhalb der Grenzen stattfand.

    – Nav

    30. November 2020 um 8:18 Uhr

Der Zugriff auf ein Array auserhalb der Grenzen gibt keinen
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

  • Woher bekommen Sie if davon an der Adresse des Arrays[3] und Array[4]da ist “nichts wirklich wichtiges”??

    – Namensnull

    9. September 2013 um 10:08 Uhr

1647300617 27 Der Zugriff auf ein Array auserhalb der Grenzen gibt keinen
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

  • Hinweis für Leser: Veraltete Antwort. Seit C++11 sollte es so sein #include<array> und std::array aus der Standardbibliothek anstelle der Boost-Äquivalente.

    – Benutzer17732522

    6. März um 16:24 Uhr


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.

  • Hinweis für Leser: Veraltete Antwort. Seit C++11 sollte es so sein #include<array> und std::array aus der Standardbibliothek anstelle der Boost-Äquivalente.

    – Benutzer17732522

    6. März um 16:24 Uhr


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.

  • Tatsächlich ist Valgrind ziemlich schlecht darin, falsche Array-Zugriffe auf den Stack zu ermitteln. (und das Beste, was es tun kann, ist, den gesamten Stapel als gültigen Schreibort zu markieren.)

    – Falaina

    6. August 2009 um 16:24 Uhr

  • @Falaina – guter Punkt, aber Valgrind kann zumindest einige Stapelfehler erkennen.

    – Todd Stout

    6. August 2009 um 17:35 Uhr

  • Und valgrind wird nichts Falsches am Code sehen, da der Compiler schlau genug ist, das Array wegzuoptimieren und einfach eine wörtliche 3 und 4 auszugeben. Diese Optimierung erfolgt, bevor gcc die Array-Grenzen überprüft, weshalb gcc die Out-of-Bounds-Warnung tut haben wird nicht angezeigt.

    – Goswin von Brederlow

    17. Januar 2019 um 15:08 Uhr

1003410cookie-checkDer Zugriff auf ein Array außerhalb der Grenzen gibt keinen Fehler, warum?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy