Инициализация int влияет на возвращаемое значение функции

Извините за неопределенность названия этого вопроса, но я не уверен, как именно это задать.

Следующий код, выполняемый на микропроцессоре Arduino (с ++ скомпилирован для микропроцессора ATMega328), работает нормально. Возвращаемые значения отображаются в комментариях в коде:

// 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"
}

Это выдает «2» в качестве позиции первой точки с запятой, как и ожидалось.

Однако, если я изменю первую строкуdetectSemicolon функция кint i; т.е. без явной инициализации у меня возникают проблемы. В частности, выводом будет «i = 0» (хорошо), «найдено при i = 2» (хорошо), «-999» (плохо!).

Таким образом, функция возвращает -999, несмотря на то, что выполнила инструкцию print непосредственно передreturn 2; и несмотря на то, что оператор print никогда не выполнялся непосредственно передreturn -999; линия.

Может ли кто-нибудь помочь мне понять, что здесь происходит? Я понимаю, что переменные внутри функций в c теоретически могут содержать любой старый мусор, если они не инициализированы, но здесь я специально проверяю в выражении print, что этого не произошло, и пока ...

РЕДАКТИРОВАТЬ: Спасибо всем, кто принял участие, и особенно underscore_d за их отличный ответ. Кажется, что неопределенное поведение действительно приводит к тому, что компилятор просто пропускает все, что связано сi, Вот некоторые из сборок с serial.prints в Detection Semicolon закомментированы:

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>

Похоже, что компилятор фактически полностью игнорирует цикл while и приходит к выводу, что вывод всегда будет "-999", и поэтому он даже не беспокоится о вызове функции, вместо этого он жестко кодирует 0xFC19. Я еще раз посмотрю с включенным serial.prints, чтобы функция все еще вызывалась, но я думаю, что это сильный указатель.

РЕДАКТИРОВАТЬ 2:

Для тех, кто действительно заботится, вот ссылка на дизассемблированный код, точно такой же, как показано выше (в случае UB):

https://justpaste.it/vwu8

Если вы посмотрите внимательно, кажется, компилятор назначает регистр 28 в качестве местоположенияi и "инициализировать" его в ноль в строкеd8, Этот регистр обрабатывается так, как если бы он содержалi в циклах while, в операторах if и т. д., поэтому кажется, что код работает, и операторы print выводятся, как и ожидалось (например, строка 122, где «i» увеличивается).

Однако, когда дело доходит до возврата этой псевдопеременной, это слишком далеко для нашего проверенного и испытанного компилятора; он рисует линию и выводит нас в другой оператор возврата (строка 120 переходит на строку 132, загружая «-999» в регистры 24 и 25, прежде чем вернуться кmain()).

Или, по крайней мере, это насколько я могу получить с моим ограниченным пониманием сборки. Мораль этой истории - странные вещи, которые происходят, когда поведение вашего кода не определено.

Ответы на вопрос(2)

Ваш ответ на вопрос