Initialising int beeinflusst den Funktionsrückgabewert

Sorry für die Unbestimmtheit des Titels dieser Frage, aber ich bin nicht sicher, wie ich das genau stellen soll.

Der folgende Code funktioniert einwandfrei, wenn er auf einem Arduino-Mikroprozessor ausgeführt wird (c ++ für einen ATMega328-Mikroprozessor kompiliert). Rückgabewerte werden in Kommentaren im Code angezeigt:

// Return the index of the first semicolon in a string
int detectSemicolon(const char* str) {

    int i = 0;

    Serial.print("i = ");
    Serial.println(i); // prints "i = 0"

    while (i <= strlen(str)) {
        if (str[i] == ';') {
            Serial.print("Found at i = ");
            Serial.println(i); // prints "Found at i = 2"
            return i;
        }
        i++;
    }

    Serial.println("Error"); // Does not execute
    return -999;
}

void main() {
    Serial.begin(250000);
    Serial.println(detectSemicolon("TE;ST")); // Prints "2"
}

Dies gibt erwartungsgemäß "2" als Position des ersten Semikolons aus.

Allerdings, wenn ich die erste Zeile des @ ändedetectSemicolon Funktion zuint i; d.h. ohne die explizite Initialisierung bekomme ich Probleme. Insbesondere lautet die Ausgabe "i = 0" (gut), "Gefunden bei i = 2" (gut), "-999" (schlecht!).

So gibt die Funktion -999 zurück, obwohl die print-Anweisung unmittelbar vor einem @ ausgeführt wurreturn 2; -Zeile und obwohl die print-Anweisung nie unmittelbar vor dem @ ausgeführt wurreturn -999; Linie

Kann mir jemand helfen zu verstehen, was hier passiert? Ich verstehe, dass Variablen in Funktionen in c theoretisch irgendeinen alten Müll enthalten können, es sei denn, sie werden initialisiert, aber hier überprüfe ich ausdrücklich in einer print-Anweisung, dass dies noch nicht geschehen ist, und doch ...

EDIT: Vielen Dank an alle, die eingechipt haben, und insbesondere an Underscore_d für ihre großartige Antwort. Es scheint, als würde undefiniertes Verhalten tatsächlich dazu führen, dass der Compiler alles überspringt, was mit @ zu tun hai. Hier sind einige der Assemblys mit den serial.prints in detectSemicolon auskommentiert:

void setup() {
    Serial.begin(250000);
    Serial.println(detectSemicolon("TE;ST")); // Prints "2"
  d0:   4a e0           ldi r20, 0x0A   ; 10
  d2:   50 e0           ldi r21, 0x00   ; 0
  d4:   69 e1           ldi r22, 0x19   ; 25
  d6:   7c ef           ldi r23, 0xFC   ; 252
  d8:   82 e2           ldi r24, 0x22   ; 34
  da:   91 e0           ldi r25, 0x01   ; 1
  dc:   0c 94 3d 03     jmp 0x67a   ; 0x67a <_ZN5Print7printlnEii>

Es sieht so aus, als würde der Compiler die while-Schleife völlig ignorieren und daraus schließen, dass die Ausgabe immer "-999" sein wird, und sich daher nicht einmal um einen Aufruf der Funktion kümmern, sondern stattdessen 0xFC19 hart codieren. Ich werde noch einmal nachsehen, ob die serial.prints aktiviert sind, damit die Funktion immer noch aufgerufen wird, aber das ist ein starker Zeiger, denke ich.

EDIT 2:

Für diejenigen, die es wirklich interessieren, hier ist ein Link zum zerlegten Code, genau wie oben gezeigt (im UB-Fall):

https: //justpaste.it/vwu

Wenn Sie genau hinschauen, scheint der Compiler das Register 28 als Speicherort von @ zu kennzeichnei und "initialisieren" es auf Null in Zeiled8. Dieses Register wird so behandelt, als ob es @ enthäli während der gesamten while-Schleife, if-Anweisungen usw., weshalb der Code zu funktionieren scheint und die print-Anweisungen wie erwartet ausgegeben werden (z. B. Zeile 122, in der "i" erhöht wird).

Wenn es jedoch darum geht, diese Pseudovariable zurückzugeben, ist dies für unseren bewährten Compiler ein Schritt zu weit. Es zeichnet die Zeile und führt uns zur anderen return-Anweisung (Zeile 120 springt zu Zeile 132 und lädt "-999" in die Register 24 und 25, bevor es zu @ zurückkehrmain()).

Oder zumindest ist das so weit, wie ich mit meinem begrenzten Verständnis der Montage kommen kann. Die Moral der Geschichte ist seltsam, was passiert, wenn das Verhalten Ihres Codes undefiniert ist.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage