Frage zu undefiniertem Verhalten (bezüglich des letzens verlinkten Artikels)
-
Hey, nachdem ich den gestern hier verlinkten Artikel (Hier) über undefiniertes Verhalten in C/C++ gelesen hab, ist mir eine Frage gekommen:
Als Beispiel für eine "Type 3 Function", also eine Funktion, die immer undeiniertes Verhalten an den Tag legt, steht:For example, a function that — regardless of input — uses a variable without initializing it is easy to unintentionally write.
Heißt das jetzt: jedesmal, wenn ich eine nicht initialisierte Variable vewende, ist das undefiniertes Verhalten?
unsigned function() { int i; return i; //definiert? }Darf der Compiler meien Funktion wegoptimieren?

Was ich dazu im C++-Standard gefunden hab, hat mich irgendwie nicht schlauer werden lassen. Da steht unter 4.10An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program
that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is
not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has
undefined behavior.
-
"undefiniertes Verhalten" bedeutet, daß alles passieren kann. Und wenn er beim Aufruf die Definition vor sich hat (Stichwort: Inlining), kann er die Funktion sicher auch komplett wegoptimieren.
Im Normalfall wird es die Funktion trotzdem geben, weil der Compiler irgendwas für den Linker zur Verfügung stellen muß.
-
Eine lokale nicht initialisierte Variabel hat keinen definierten Wert und die Verwendung dieser Variabel führt somit zu UB. Somit ist auch der Rückgabewert deiner Beispielfunktion undefiniert.
-
asdfasd schrieb:
Hey, nachdem ich den gestern hier verlinkten Artikel (Hier) über undefiniertes Verhalten in C/C++ gelesen hab, ist mir eine Frage gekommen:
Als Beispiel für eine "Type 3 Function", also eine Funktion, die immer undeiniertes Verhalten an den Tag legt, steht:For example, a function that — regardless of input — uses a variable without initializing it is easy to unintentionally write.
Heißt das jetzt: jedesmal, wenn ich eine nicht initialisierte Variable vewende, ist das undefiniertes Verhalten?
unsigned function() { int i; return i; //definiert? }Darf der Compiler meien Funktion wegoptimieren?

Was ich dazu im C++-Standard gefunden hab, hat mich irgendwie nicht schlauer werden lassen. Da steht unter 4.10An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program
that necessitates this conversion is ill-formed. If the object to which the lvalue refers is not an object of type T and is
not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has
undefined behavior.Das ist schon mal ein Anfang. Die Frage, die sich stellt, ist, ob obiges return-statement eine solche Konvertierung bedingt.
Um das herauszufinden, schauen wir als erstes, was der Standard zu return zu sagen hat (6.6.3):
A return statement with an expression of non-void type can be used only in functions returning a value; the value of the expression is
returned to the caller of the function. The expression is implicitly converted to the return type of the function in which it appears.Der Typ des Rückgabewertes ist int. Der Typ des Argumentes is signed. Also brauchen wir schon mal eine integrlae Konvertierung int->unsigned (4.7). Diese Konvertierung arbeite allerdings nur mit rvalues, also muss das lvalue zunächst in ein rvalue konvertiert werden (4.1). Die gesamte Konvertierungsprozess ergibt damit eine Standardkonvertierungssequenz (Abschnitt 4).
-
löälöä schrieb:
Eine lokale nicht initialisierte Variabel hat keinen definierten Wert und die Verwendung dieser Variabel führt somit zu UB. Somit ist auch der Rückgabewert deiner Beispielfunktion undefiniert.
Was genau meinst du mit Verwendung? Wäre denn das hier definiert?
unsigned i; //nicht initialisierte, lokale Variable i = 3+4; //Zuweisung legal?Abgesehen davon, dass der Kompiler das optimieren wird - wird jetzt i in deinem Sinne "verwendet"?
-
asdfasd schrieb:
löälöä schrieb:
Eine lokale nicht initialisierte Variabel hat keinen definierten Wert und die Verwendung dieser Variabel führt somit zu UB. Somit ist auch der Rückgabewert deiner Beispielfunktion undefiniert.
Was genau meinst du mit Verwendung? Wäre denn das hier definiert?
In dem Zusammenhang ist Verwendung im Sinne von "Wert verwenden" gemeint - das heißt die Zuweisung auf eine nicht-initialisierte Variable ist klar definiert (Anweisungen wie
i+=5;oder++i;sind dagegen undefiniert, wenn i nicht initialisiert ist).
-
Ah, sehr gut!
Danke an alle, die so schnell und kompetent geantwortet haben
-
Wir können das Ganze interessanter machen. Wir nehmen an, dass unsigned keine Traprepräsentation hat.
unsigned foo() { unsigned i; std::memmove( &i, &i, sizeof i ); return i; }definiert oder nicht?

-
Es ist ein klein wenig komplizierter. Der Standard unterscheidet zwischen undefiniertem und unspezifiziertem Verhalten.
Undefiniertes Verhalten bedeutet, dass der Standard keinerlei Bedingungen für das Verhalten des Programms setzt - Buffer-Overflows, wilde Zeiger und so fallen in diese Kategorie.
Unspezifiziertes Verhalten betrifft korrekte Programme, deren Verhalten von der Implementation abhängt. Dieses Verhalten muss nicht dokumentiert sein. Uninitialisierte Variablen fallen unter Umständen darunter. Zum Beispiel ist
#include <iostream> int main() { int i; std::cout << i << std::endl; }ein gültiges Programm, aber was es ausgibt, ist vom Standard nicht vorgegeben. Dagegen erzeugt
#include <iostream> int main() { int i; int array[10] = { }; std::cout << array[i] << std::endl; }undefiniertes Verhalten (sofern die Implementation für i nicht einen Wert zwischen 0 und 9 spezifiziert).
So ist das Verhalten von campers foo-Funktion definiert, aber der Rückgabewert ist nicht spezifiziert. Es ist dann natürlich leicht möglich, mit unspezifizierten Werten undefiniertes Verhalten zu erzeugen.
Ref. ISO/IEC 14882:2003 1.3.11 und 1.3.12.
-
seldon schrieb:
Zum Beispiel ist
#include <iostream> int main() { int i; std::cout << i << std::endl; }undefiniert. Der Funktionsaufruf (<<) bedingt ein l- zu rvalue Konvertierung für das Argument i, um den 2. Funktionsparameter zu initialisieren, und das führt zu UB (4.1).
#include <iostream> int main() { int i; int array[10] = { }; std::cout << array[i] << std::endl; }undefiniertes Verhalten (sofern die Implementation für i nicht einen Wert zwischen 0 und 9 spezifiziert).
Ebenso unbedingt undefiniert. der []-Operator benötigt rvalues als Operanden.
So ist das Verhalten von campers foo-Funktion definiert, aber der Rückgabewert ist nicht spezifiziert.
Das ist die Frage. Jedenfalls lassen sich auch Argumente dafür finden, dass das Ganze undefiniert wäre.
-
camper schrieb:
So ist das Verhalten von campers foo-Funktion definiert, aber der Rückgabewert ist nicht spezifiziert.
Das ist die Frage. Jedenfalls lassen sich auch Argumente dafür finden, dass das Ganze undefiniert wäre.
Machs doch nicht so spannend, was fällt dir da z.B. ein?
Ich kann mir grad nix vorstellen.
-
Warum überleg ihr, was passiert, wenn man Müll programmiert?