Undefiniertes Verhalten (UB)
-
mal sehen:
- convertTemperature gibt keinen wert zurück
- formatTemperature gibt einen zeiger auf ungültigen speicher zurück
- formatTemperature verursacht sehr wahrscheinlich einen überlauf
- strncpy kopiert kein terminatorzeichen (sofern ich mich jetzt nicht 3 mal verzählt habe)
-
Der cast beim
malloc
(void*
aufvoid*
) sollte einem auch zu denken geben.Und dann noch stdlib.h einbinden.
-
@DirkB sagte in Undefiniertes Verhalten (UB):
Der cast beim
malloc
(void*
aufvoid*
) sollte einem auch zu denken geben.Und dann noch stdlib.h einbinden.
Nun, ohne das include von stdlib.h ist es ja ein Cast von
int
nachvoid*
. Was schlimm ist. Bloß weiß ich jetzt ernsthaft nicht, ob das einer der Fehler ist, die man finden soll, oder einfach nicht gekonnt. Denn viel zu viele Lehrer können es einfach nicht richtig.
-
@pcovc
Ein kleiner Tipp. Versuche doch einmal deinen Code zu kompilieren.Schnappe dir QT (gibts als Open Source) und installiere QT mit dem GCC (ist im Setup enthalten).
Danach erzeugst du mittels QT Creator ein neues Projekt und kopierst den Code in dein Projekt. Wenn du danach versuchst den Code zu kompilieren, erhältst du viele nützliche Warnungen und Fehler.
main.c:5: Warnung: unused variable 'celsius' main.c:6: Warnung: control reaches end of non-void function main.c:11: Warnung: address of stack memory associated with local variable 'result' returned main.c:15: Warnung: implicitly declaring library function 'malloc' with type 'void *(unsigned long long)' main.c:15: include the header <stdlib.h> or explicitly provide a declaration for 'malloc' main.c:10: Warnung: ' degree ' directive writing 8 bytes into a region of size between 0 and 2 [-Wformat-overflow=] sprintf(result, "%f degree %s", value, unit); ...
-
Danke für alle antworten
@Quiche-Lorraine sagte in Undefiniertes Verhalten (UB):Schnappe dir QT (gibts als Open Source) und installiere QT mit dem GCC (ist im Setup enthalten).
Das ist der Hammer! Danke
-
@Quiche-Lorraine sagte in Undefiniertes Verhalten (UB):
main.c:10: Warnung: ' degree ' directive writing 8 bytes into a region of size between 0 and 2 [-Wformat-overflow=]
sprintf(result, "%f degree %s", value, unit);Woah! Ist noch irgendjemand so beeindruckt wie ich von dieser Diagnose? Als ich mit C angefangen habe, hat der GCC so gerade halbwegs zuverlässig vor falschen Formatspezifizierern gewarnt. Das hier sind 3 Indirektionen an Formatanalyse.
-
-
@Quiche-Lorraine sagte in Undefiniertes Verhalten (UB):
@pcovc
Ein kleiner Tipp. Versuche doch einmal deinen Code zu kompilieren.Schnappe dir QT (gibts als Open Source) und installiere QT mit dem GCC (ist im Setup enthalten).
Danach erzeugst du mittels QT Creator ein neues Projekt und kopierst den Code in dein Projekt. Wenn du danach versuchst den Code zu kompilieren, erhältst du viele nützliche Warnungen und Fehler.
Um mal die Verwirrung der Forums-Kollegen hier zu erläutern:
Was du beobachtest hat mit QT rein gar nichts zu tun. QT ist letztlich einfach nur ein Framework und der QTCreator ist einfach nur eine Entwicklungsumgebung.
QT gibt es z.B. auch für Python. In dem Fall würde dir QT gar nichts bringen.Was hier den Unterschied macht: Die Compiler-Einstellungen mit denen du dein Programm kompilierst. Die sind bei einigen Umgebungen voreingestellt, so dass man als Frischling versucht ist, zu glauben, das gewisse Fehlermeldungen oder Warnungen mit der Entwicklungsumgebung zu tun haben, was allerdings nicht der Fall ist.
Compiler Flags siehe auch: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Es gibt auch C-Spezifische Warnungen. Damit kenne ich mich allerdings nicht so aus, da ich kein C programmiere.Wenn du mit "-wall -wextra" kompilierst, machst du erstmal nicht viel falsch, bekommst aber vielleicht auch ein paar Warnungen, die für dich nicht relevant sind. Aber besser zu viele als zu wenige. Mit steigender Programmiererfahrung kannst du da auch mehr ins Detail gehen und dir den Compiler präziser einstellen.
-
@DirkB sagte in Undefiniertes Verhalten (UB):
@Quiche-Lorraine sagte in Undefiniertes Verhalten (UB):
Schnappe dir QT
Bringt QT hier irgendetwas?
Wenn du wirklich ein Interesse daran hast, anderen Leuten zu helfen, bringt so ein hingerotzter nichtssagender Satz rein gar nichts. Wenn du nicht helfen willst, was auch ok ist, dann schreib einfach nichts.
-
@SeppJ sagte in Undefiniertes Verhalten (UB):
Als ich mit C angefangen habe, hat der GCC so gerade halbwegs zuverlässig vor falschen Formatspezifizierern gewarnt.
Aber nicht jeder nutzt GCC sondern auch mal ein Visual Studio 20013. Und dieser möchte anstatt sprintf sprintf_s nutzen. Da gefallen mir die Meldungen vom GCC besser, insbesonders da ich die sprintf_s Funktion(en) nicht mag.
@DirkB
@It0101
Das ist mir klar. QT ist in diesem Fall nur als eine gute IDE mit einigermaßen aktuellem und scharf eingestelltem GCC gedacht .
-
@Quiche-Lorraine sagte in Undefiniertes Verhalten (UB):
@It0101
Das ist mir klar. QT ist in diesem Fall nur als eine gute IDE mit einigermaßen aktuellem und scharf eingestelltem GCC gedacht .Verstehe ich. Aber die Entwicklungsumgebung hat halt mit dem Problem nichts zu tun. Für Frischlinge ist es natürlich klasse, wenn man ein funktionierendes voreingestelltes System hat. Ging mir früher auch so, als ich von Compiler-Switches noch nichts wusste.
Der Hinweis auf Compilerschalter wäre in dem Fall sinnvoller gewesen als die Nennung einer anderen IDE.
-
@It0101 sagte in Undefiniertes Verhalten (UB):
Wenn du wirklich ein Interesse daran hast, anderen Leuten zu helfen, bringt so ein hingerotzter nichtssagender Satz rein gar nichts. Wenn du nicht helfen willst, was auch ok ist, dann schreib einfach nichts.
Die Frage hat etwas gebracht. (Siehe die Diskussion)
Wenn QT etwas gebracht hätte, hätte ich etwas dazu gelernt (mir wäre geholfen worden)
-
@Quiche-Lorraine sagte in Undefiniertes Verhalten (UB):
QT ist in diesem Fall nur als eine gute IDE mit einigermaßen aktuellem und scharf eingestelltem GCC gedacht .
Qt ist aber das Framework, die IDE ist QtCreator - wenn dann benutze auch die richtigen Ausdrücke.
Aber wie @It0101 geschrieben hat, geht es um die eingestellten Compiler-Optionen! @pcovc hat ja noch nicht einmal geschrieben, mit welcher IDE bzw. Compiler er bisher den Code compiliert hat.
-
mir scheint es eher so, dass die aufgabe in einem pdf steht und es heißt "finden sie mal die fehler!"
-
Man wird ja wohl GCC sogar online für so ein Miniprogramm nutzen können und hat dann in 5 Minuten diese Warnungen als Anhaltspunkt. Also nicht jeder hat GCC klingt dann bissl nach Ausflucht.
-
man könnte auch vernünftig die sprache lernen, damit man solche fehler direkt sieht. an dem programm herumzupfuschen, bis der compiler zufällig mal keine warnungen oder fehler mehr ausgibt, ist ein schlechter ansatz imho.
-
@Wade1234 sagte in Undefiniertes Verhalten (UB):
man könnte auch vernünftig die sprache lernen, damit man solche fehler direkt sieht.
Ich wage mal die Vermutung, dass es bei der Aufgabenstellung genau darum geht
-
@SeppJ sagte in Undefiniertes Verhalten (UB):
@DirkB sagte in Undefiniertes Verhalten (UB):
Der cast beim
malloc
(void*
aufvoid*
) sollte einem auch zu denken geben.Und dann noch stdlib.h einbinden.
Nun, ohne das include von stdlib.h ist es ja ein Cast von
int
nachvoid*
.Nein, veraltete Ansicht. Seit C99 gibts kein 'implicit int' mehr.
Denn viel zu viele Lehrer können es einfach nicht richtig.
Natürlich.
-
@Wutz sagte in Undefiniertes Verhalten (UB):
@SeppJ sagte in Undefiniertes Verhalten (UB):
@DirkB sagte in Undefiniertes Verhalten (UB):
Der cast beim
malloc
(void*
aufvoid*
) sollte einem auch zu denken geben.Und dann noch stdlib.h einbinden.
Nun, ohne das include von stdlib.h ist es ja ein Cast von
int
nachvoid*
.Nein, veraltete Ansicht. Seit C99 gibts kein 'implicit int' mehr.
Korrekt, aber dummerweise übersetzen GCC, Clang, ICC, MSVC (und wahrscheinlich noch viele mehr) es trotzdem und warnen bloß. MSVC warnt noch nicht einmal.
-
@pcovc sagte in Undefiniertes Verhalten (UB):
#include <stdio.h> #include <string.h> double convertTemperature(double* fahrenheit) { double celsius = (*fahrenheit - 32)/1.8; } char* formatTemperature(double value, const char* unit) { char result[10]; sprintf(result, "%f degree %s", value, unit); return result; } int main() { void* buffer = (void*)malloc(33); strncpy(buffer, "enter a temperature in degree F: ", 33); //Kopiert 33 zeichen von "enter a...." in buffer. printf("%s", buffer); scanf("%f", buffer); double celsius = convertTemperature(buffer); printf("the temperatures is %s\n", formatTemperature(celsius, "C")); }
- Zeile 15 weist ein undefiniertes verhalten auf, da man kein Typ definiert der den String entgegenimmt bei Zeile 16.
- Zeile 17 gibt nicht das erwartete raus, da es keinen String bekommen hat
- Zeile 19 möchte einen double obwohl buffer void ist und ein String übergeben bekommen hat.
- Zeile 20 geben wir einen Array obwohl ein Pointer verlangt wird.
- 21 führt zu einem UB, da in Zeile 9-11 ein Array genutzt wird obwohl die funktion sprintf ein Pointer möchte.
Du (oder dein Lehrer und du jetzt auch) hast da völlig falsche Vorstellungen von UB.
UB (undefined behavior) ist ein Fachbegriff aus dem C-Standard (http://www.iso-9899.info/n1570.html) und definiert das (Laufzeit)Verhalten eines Programms, das mit einem standardkonformen Compiler übersetzt wurde bei speziellen Quellcodesituationen.
Für UB sind die dort alle detailliert aufgelistet und entweder explizit als UB gekennzeichnet oder implizit (mittelsshall
odershall not
).
Was du hier machst ist das Pferd von hinten aufzuzäumen, denn o.g. UB-Situationenkönnen
in solche bufferoverflows münden, aber daraus zu schlußfolgern, dass alle bufferoverflows aus UB resultieren, ist laienhafter Unsinn.
Ich tippe mal darauf, dass dein Lehrer irgendwo den Fachbegriff UB aufgeschnappt hat und meint, ihn verstanden zu haben und seine Schäfchen damit beglücken zu müssen.- Zeile15: ist vollkommen korrekt und niemals UB, obwohl kein C-Profi malloc casten würde
- Zeile16: ist ebenso kein UB, obwohl kein C-Profi strncpy verwenden würde, zumindest nicht, wenn er in Betracht ziehen muss, dass jemand anderes als er selbst den Code warten muss; durch das implizite Anhängen von '\0' an den 33-Byte Speicher kommt es zu einem (sogar schreibenden) Zugriff auf undefinierten Speicher; gute Compiler produzieren hier Code, der vom Laufzeitsystem in segmentation fault o.ä. mündet; auch valgrind & Co. müssten sowas finden; abseits von UB im Standard gibt es aber (natürlich nur in Profiquellen und nicht beim Lehrer) best practices, die auf solche Fehlerquellen hinweisen http://www.cplusplus.com/reference/cstring/strncpy/; C-Profis haben sowas sämtlichst im Kopf, brauchen nicht nachzuschlagen und unterscheiden sich auch dadurch von selbsternannten Programmierprofis
- Zeile17: kein UB, aber printf greift auch hier aus o.g. Gründen auf undefinierten Speicher zu (wenn auch nur lesend)
- Zeile19: auch hier ist nirgendwo UB, buffer ist vom Typ void*, scanf verlangt hier float*; da void* aber zu allen Datenzeigertypen kompatibel ist, liegt kein UB vor; wenn hier was schiefgehen kann ist es, falls der malloc-Speicher kleiner sizeof(float) gewählt wurde, auch dann kommt es zum (schreibenden) Zugriff auf undefinierte Speicherbereiche, was aber der Standard mit UB gar nicht festlegt, weil es sich auch hier um Laufzeitverhalten handelt
- Zeile20: kein UB; hier fehlt ja wohl das return in
convertTemperature
: gute Compiler warnen hier (dead code oder dergleichen) - Zeile21: kein UB; char[10] dürfte zu gering dimensioniert sein, sodass der schreibende Zugriff auf undefinierten Speicher schon vor dem return passiert; ansonsten ist die o.g. Compilermeldung natürlich korrekt
Fazit: der C-Sprachstandard regelt mittels UB das Verhalten von Compilern in einzelnen Codesituationen, was sie zu tun oder zu lassen haben (im Falle von UB können die Compiler Code produzieren, wie sie wollen).
Für allgemeine Programmiertipps ist der Standard der falsche Ort, der beschränkt sich (z.B. für bufferoverflows) nur auf allgemeine Ratschläge: Bounds-checking interfaces
Der Standard regelt somit in erster Linie Vorschriften (UB u.a.) für Compilerbauer, nicht für Programmierer.