La inicialización int afecta el valor de retorno de la función

Perdón por la vaguedad del título de esta pregunta, pero no estoy seguro de cómo preguntar esto exactamente.

El siguiente código, cuando se ejecuta en un microprocesador Arduino (c ++ compilado para un microprocesador ATMega328) funciona bien. Los valores de retorno se muestran en los comentarios en el código:

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

Esto genera "2" como la posición del primer punto y coma, como se esperaba.

Sin embargo, si cambio la primera línea deldetectSemicolon funcionar paraint i; es decir, sin la inicialización explícita, tengo problemas. Específicamente, el resultado es "i = 0" (bueno), "Encontrado en i = 2" (bueno), "-999" (¡malo!).

Por lo tanto, la función devuelve -999 a pesar de haber ejecutado la declaración de impresión inmediatamente antes de unreturn 2; línea y a pesar de nunca ejecutar la declaración de impresión inmediatamente antes de lareturn -999; línea.

¿Alguien puede ayudarme a entender lo que está pasando aquí? Entiendo que las variables dentro de las funciones en c pueden contener teóricamente cualquier basura vieja a menos que se inicialicen, pero aquí estoy comprobando específicamente en una declaración impresa que esto no ha sucedido, y aún ...

EDITAR: Gracias a todos los que han contribuido, y particularmente a subrayar_d por su gran respuesta. Parece que el comportamiento indefinido está causando que el compilador omita cualquier cosa que impliquei. Aquí hay algunos de los ensamblados con serial.prints dentro de detectSemicolon comentados:

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>

Parece que el compilador en realidad ignora por completo el ciclo while y concluye que la salida siempre será "-999", por lo que ni siquiera se molesta con una llamada a la función, sino que codifica 0xFC19. Echaré otro vistazo con el serial.prints habilitado para que la función aún se llame, pero creo que este es un puntero fuerte.

EDITAR 2:

Para aquellos que realmente se preocupan, aquí hay un enlace al código desmontado exactamente como se muestra arriba (en el caso de UB):

https://justpaste.it/vwu8

Si observa detenidamente, el compilador parece estar designando el registro 28 como la ubicación dei y "inicializarlo" a cero en línead8. Este registro se trata como si contuvierai a lo largo de los bucles while, sentencias if, etc., por lo que el código parece funcionar y las sentencias de impresión salen como se esperaba (por ejemplo, la línea 122 donde se incrementa "i").

Sin embargo, cuando se trata de devolver esta pseudovariable, este es un paso demasiado lejos para nuestro compilador probado y probado; dibuja la línea y nos devuelve a la otra declaración de retorno (la línea 120 salta a la línea 132, cargando "-999" en los registros 24 y 25 antes de volver amain())

O al menos, eso es lo más lejos que puedo llegar con mi limitado dominio del ensamblaje. La moraleja de la historia es que cosas extrañas suceden cuando el comportamiento de su código no está definido.

Respuestas a la pregunta(2)

Su respuesta a la pregunta