Ist der Segmentierungsfehler tatsächlich undefiniertes Verhalten, wenn wir auf ein nicht statisches Datenelement verweisen

Lesezeit: 5 Minuten

Ich hatte die folgende Regel gelesen und ich habe versucht, ein Beispiel zu schreiben, das eine widerspiegelt. Die Regel ist ab 3.8/5 N3797:

Bevor die Lebensdauer eines Objekts begonnen hat, aber nachdem der Speicher, den das Objekt belegen wird, zugewiesen wurde, oder, nachdem die Lebensdauer eines Objekts beendet ist und bevor der Speicher, den das Objekt belegt hat, wiederverwendet oder freigegeben wird, jeder Zeiger, der auf den Speicher verweist Ort, an dem sich das Objekt befinden wird oder befand, darf verwendet werden, jedoch nur in begrenztem Umfang. Für ein im Bau befindliches oder zerstörtes Objekt siehe 12.7. Andernfallsbezieht sich ein solcher Zeiger auf zugewiesenen Speicher (3.7.4.2) und verwendet den Zeiger, als ob der Zeiger vom Typ wäre void* ist wohldefiniert. Die Indirektion durch einen solchen Zeiger ist zulässig, aber der resultierende lvalue darf nur eingeschränkt verwendet werden, wie unten beschrieben. Das Programm hat undefiniertes Verhalten, wenn:

[…]

— Der Zeiger wird verwendet, um auf ein nicht statisches Datenelement zuzugreifen oder eine nicht statische Elementfunktion des Objekts aufzurufen, oder

[…]

Das Beispiel, für das ich geschrieben habe:

#include <iostream>
#include <typeinfo>

using std::cout;
using std::endl;

struct A
{
    int b = 5;
    static const int a = 5;
};

int main()
{
    A *p = (A*)0xa31a3442;
    cout << p -> a; //1, Well-fromed, there is no compile-time error
    cout << p -> b; //2, Segmentation fault is producing
}

Stimmt das in dem Fall //1 ist wohlgeformt und verursacht keine UBaber //2 erzeugter Segmentierungsfehler, der ist UB?

  • Ich bin mir nicht sicher, was Sie mit “tatsächlichem” undefiniertem Verhalten meinen. Der Standard ist ziemlich klar: (1) ist nicht UB und (2) ist UB.

    – David Titarenco

    3. September 2014 um 5:25 Uhr


  • @DavidTitarenco Der Standard enthält ein Beispiel, das UB nach der Wiederverwendung des Speichers demonstriert. Aber nicht für einen, den ich bereitgestellt habe.

    Benutzer2953119

    3. September 2014 um 5:29 Uhr

  • Eigentlich sagt die Norm “wird sein oder war.” Beachte die Disjunktion und das “wird sein“. Da wir die Zeitreise noch nicht perfektioniert haben, haben Sie keine Möglichkeit zu wissen, wo sich der Speicherort eines Objekts befinden wird – was es funktional gleichbedeutend mit der Auswahl eines beliebigen Speicherorts macht.

    – David Titarenco

    3. September 2014 um 5:34 Uhr


Ist der Segmentierungsfehler tatsachlich undefiniertes Verhalten wenn wir auf ein
Basile Starynkevitch

Undefiniertes Verhalten bedeutet, dass irgendetwas kann bei einer standardkonformen Implementierung passieren. Wirklich alles. (und Ihr Punkt 2 ist UB)

Eine Umsetzung könnte

  • Ihren Computer explodieren lassen und Sie körperlich verletzen
  • ein Schwarzes Loch machen, das das gesamte Sonnensystem verschlingt
  • nichts Ernstes tun
  • leuchten einige LED auf Ihrer Tastatur
  • Machen Sie eine Zeitreise und töten Sie alle Ihre Großeltern vor der Geburt Ihrer eigenen Eltern
  • etc….

und konform sein (im Falle von UB); Lesen Sie auch über die bekanntere Idee von nasale Dämonen.

Was also auf UB passiert, ist nicht vorhersehbar und (im Allgemeinen) nicht reproduzierbar.

Denken Sie ernsthafter darüber nach, was UB in dem Computer bedeuten könnte, der mit den ABS-Bremsen Ihres Autos verbunden ist, oder in einigen Künstliches Herzoder ein Kernkraftwerk fahren.

Insbesondere könnte es manchmal funktionieren. Da die meisten Betriebssysteme haben ASLR Ihr Code hat eine winzige Chance zu funktionieren (zB if 0xa31a3442 zufällig auf einige hindeuten gültig Speicherort, zB auf dem Stack, aber das wird beim nächsten Durchlauf nicht mehr reproduziert!)

UB ist eine Möglichkeit, Implementierern (z. B. von Compilern oder Betriebssystemen) und Computern die Freiheit zu geben, zu tun, was sie “wollen”, mit anderen Worten, sich nicht um die Konsequenzen zu kümmern. Dies ermöglicht zB clevere Optimierungen oder nette Umsetzungstricks. Aber Sie sollte sich darum kümmern (und die Konsequenzen sind anders, wenn Sie das eingebettete Flugsteuerungssystem eines Flugzeugs codieren, oder nur ein paar hackige Demo-Beleuchtungs-LEDs mit einem RasberryPi oder ein einfaches Beispiel für einen C++-Kurs, der unter Linux läuft).

Denken Sie daran, dass Sprachstandards nicht einmal Computer (oder Hardware) für die Implementierung benötigen: Sie könnten Ihren C++-Code mit einem Team menschlicher Sklaven “ausführen”, aber das wäre höchst unethisch (und kostspielig und unzuverlässig).

Siehe auch Hier für weitere Referenzen. Du solltest zumindest lesen Lattners Blog auf Undefiniertes Verhalten (Das meiste, was er für C geschrieben hat, gilt für C++ und viele andere Sprachen mit UB).


(hinzugefügt im Dezember 2015 & Juni 2016)

NB. Die Valgrind Werkzeug und diverses -fsanitize= Debugging-Optionen für kürzlich GCC oder Clang/LLVM sind ganz nützlich. Aktivieren Sie außerdem alle Warnungen und Debug-Informationen in Ihrem Compiler (z g++ -Wall -Wextra -g) und angemessen verwenden Instrumentierungsmöglichkeiten wie zum Beispiel -fsanitize=undefined. Beachten Sie, dass es unmöglich ist, zur Kompilierzeit statisch und vollständig zu erkennen alle Fälle von UB (das wäre gleichbedeutend mit der Halteproblem).

PS. Die obige Antwort ist nicht spezifisch für C++; es passt auch für C!

  • Aber es gibt ein klassisches Paradoxon, wenn sie bei Zeitreisen alle getötet werden, bevor sie ihre eigenen Eltern zur Welt bringen. Das ist das Problem, auf das ich mich bezog.

    – Basile Starynkevitch

    3. September 2014 um 7:49 Uhr


  • @Destructor: dann hätte ich eine Voreingenommenheit gegen Frauen, die auf StackOverflow posten. Diese Voreingenommenheit versuche ich zu vermeiden.

    – Basile Starynkevitch

    4. Mai 2016 um 8:10 Uhr

Du hast gefragt:

Stimmt es, dass in dem Fall //1 wohlgeformt ist und kein UB verursacht?

Die von Ihnen zitierten Teile der Norm erwähnen nichts darüber.

Du hast auch gefragt:

aber //2 hat einen Segmentierungsfehler erzeugt, was ist UB?

Die von Ihnen zitierten Teile der Norm entsprechen nicht diesem speziellen Verhalten. Sie sehen UB wegen wo p Punkte. Es zeigt auf Speicher, der kein gültiges Objekt enthält.

1647255611 217 Ist der Segmentierungsfehler tatsachlich undefiniertes Verhalten wenn wir auf ein
ldgabbay

Regel 3.8/5 betrifft die Zeit außerhalb der Konstruktion/Zerstörung eines Objekts, aber innerhalb der Zuweisung/Freigabe des Speichers, in dem sich das Objekt befindet. Das Folgende zeigt die Punkte außerhalb der Lebensdauer eines Objekts:

void *buffer = malloc(sizeof(A));
// outside of lifetime of a
// a->b is undefined
A* a = new (buffer) A();
// within lifetime of a
// a->b is valid
a->~A();
// outside of lifetime of a
// a->b is undefined
free(buffer);

Technisch gesehen spiegelt Ihr Beitrag Regel 3.8/5 nicht wider, da Sie außerhalb seiner Lebensdauer nicht auf das Objekt zugreifen. Sie werfen einfach zufälligen Speicher als Instanz.

1001210cookie-checkIst der Segmentierungsfehler tatsächlich undefiniertes Verhalten, wenn wir auf ein nicht statisches Datenelement verweisen

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

Privacy policy