Inicializar int afeta o valor de retorno da função
Desculpe pela imprecisão do título desta pergunta, mas não sei como perguntar exatamente.
O código a seguir, quando executado em um microprocessador Arduino (c ++ compilado para um microprocessador ATMega328), funciona bem. Os valores retornados são mostrados nos comentários no 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"
}
Isso gera "2" como a posição do primeiro ponto e vírgula, conforme o esperado.
No entanto, se eu alterar a primeira linha dodetectSemicolon
função paraint i;
ou seja, sem a inicialização explícita, eu tenho problemas. Especificamente, a saída é "i = 0" (boa), "Encontrada em i = 2" (boa), "-999" (ruim!).
Portanto, a função está retornando -999, apesar de ter executado a instrução print imediatamente antes de umreturn 2;
linha e apesar de nunca executar a declaração de impressão imediatamente antes doreturn -999;
linha.
Alguém pode me ajudar a entender o que está acontecendo aqui? Eu entendo que variáveis dentro de funções em c podem teoricamente conter qualquer lixo antigo, a menos que sejam inicializadas, mas aqui estou verificando especificamente uma declaração de impressão de que isso não aconteceu e ainda ...
EDIT: Obrigado a todos que participaram, e principalmente ao underscore_d por sua ótima resposta. Parece que um comportamento indefinido está realmente fazendo com que o compilador pule qualquer coisa que envolvai
. Aqui estão algumas das montagens com o serial.prints dentro do detectSemicolon comentado:
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 o compilador está desconsiderando completamente o loop while e concluindo que a saída sempre será "-999" e, portanto, nem se preocupa com uma chamada para a função, em vez de codificar 0xFC19. Vou dar uma outra olhada com o serial.prints ativado, para que a função ainda seja chamada, mas acho que esse é um ponteiro forte.
EDIT 2:
Para quem realmente se importa, aqui está um link para o código desmontado exatamente como mostrado acima (no caso da UB):
Se você observar com cuidado, o compilador parece estar designando o registro 28 como o local dei
e "inicializando" para zero na linhad8
. Esse registro é tratado como se contivessei
durante os loops while, instruções if, etc., e é por isso que o código parece funcionar e as instruções print são exibidas conforme o esperado (por exemplo, linha 122 onde "i" é incrementado).
No entanto, quando se trata de retornar essa pseudo-variável, esse é um passo longe demais para nosso compilador testado e testado; ele desenha a linha e nos envia para a outra instrução de retorno (a linha 120 salta para a linha 132, carregando "-999" nos registros 24 e 25 antes de retornar aomain()
)
Ou, pelo menos, é o máximo que posso obter com minha compreensão limitada da montagem. A moral da história é uma coisa estranha que acontece quando o comportamento do seu código é indefinido.