Undefiniertes Verhalten (UB)



  • Hallo,
    ich habe im vorliegenden Code einige Zeilen die ein undefiniertes Verhalten des System auslösen.
    Ich weiß jedoch nicht ob mir da was fehlt oder ich etwas da falsch verstanden habe. Meine Antworten dazu sind:

    #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.


  • Mir scheint, du hast die falschen Stellen im Blick.

    @pcovc sagte in Undefiniertes Verhalten (UB):

    Zeile 15 weist ein undefiniertes verhalten auf, da man kein Typ definiert der den String entgegenimmt bei Zeile 16.

    Nein. Außerdem wäre es komisch, wenn das Verhalten davon abhängen würde, was danach kommt.

    Zeile 17 gibt nicht das erwartete raus, da es keinen String bekommen hat

    Nein. Das Problem ist, dass dein String 33 Zeichen lang ist. Somit ist kein Platz mehr für das 0-Byte, das den String beendet. Zeile 17 liest daher auch noch aus Speicher hinter dem buffer. Das ist nicht erlaubt.

    Bei dem void* als float frage ich mich gerade, ob das vom Alignment erlaubt ist.

    Das sprintf in Zeile 10 hat ebenfalls einen zu kleines result. Außerdem wird in Zeile 11 lokaler Speicher returnt, dafür returnt convertTemperature gar nichts.



  • @pcovc sagte in Undefiniertes Verhalten (UB):

    21 führt zu einem UB, da in Zeile 9-11 ein Array genutzt wird obwohl die funktion sprintf ein Pointer möchte.

    Die Funktion gibt aber einen char-Pointer zurück.

    In dem Programm ist ziemlich viel quer, aber gerade das ist die falsche Begründung.
    Das Problem ist das lokale Array.


  • Mod

    @wob sagte in Undefiniertes Verhalten (UB):

    Bei dem void* als float frage ich mich gerade, ob das vom Alignment erlaubt ist.

    void* an sich könnte theoretisch zwar auch für double falsche Ausrichtungen enthalten, aber hier kommt der tatsächliche Wert aus einem malloc und passt daher garantiert für alle Alignmentanforderungen.

    @pcovc sagte in Undefiniertes Verhalten (UB):

    Zeile 19 möchte einen double obwohl buffer void ist und ein String übergeben bekommen hat.

    Nein, es möchte einen float*, obwohl buffer ein void* ist. Es ist ihm aber völlig egal, was da vorher gestanden hat. Speicher hat keinen Typ. Ausdrücke in deinem Programm haben einen Typ, aber zur Laufzeit existiert davon nichts mehr.

    @pcovc sagte in Undefiniertes Verhalten (UB):

    Zeile 20 geben wir einen Array obwohl ein Pointer verlangt wird.

    Nochmal nein! buffer ist ein void* und kein Array und es ist ihm völlig egal, worauf er zeigt. Die Umwandung void* nach double* ist völlig legitim und geht sogar implizit. Was hier das wahre Problem ist, dass buffer in Zeile 20 auf einen float zeigt (wegen Zeile 19), aber nun so behandelt wird, als zeigte es auf einen double.

    Damit hätten wir deine sämtlichen Antworten widerlegt 🤔
    Jetzt ist natürlich noch die Frage, ob da noch mehr drin ist, das du übersehen hast. Nun, es ist deine Hausaufgabe, daher reiße ich mich nicht drum. Für dich der Tipp: Nahezu alle Antworten auf deine Frage hier drehen sich darum, dass du Arrays, Zeiger, und ihre Unterschiede nicht richtig drauf hast. Das musst du dir dringend genauer angucken. Und stell dabei sicher, dass du dir das in einem guten Buch anguckst, denn leider kapieren viele schlechte Lehrer diesen Stoff selber nicht.



  • @pcovc

    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* auf void*) sollte einem auch zu denken geben.

    Und dann noch stdlib.h einbinden.


  • Mod

    @DirkB sagte in Undefiniertes Verhalten (UB):

    Der cast beim malloc (void* auf void*) 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 nach void*. 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 😄


  • Mod

    @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):

    Schnappe dir QT

    Bringt QT hier irgendetwas?



  • @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.


Anmelden zum Antworten