この質問のタイトルの曖昧さを残念に思っていますが、これを正確に求める方法がわかりません。初期化intが関数の戻り値に影響する
次のコードは、Arduinoマイクロプロセッサ(ATMega328マイクロプロセッサ用にコンパイルされたC++)で実行すると問題なく動作します。戻り値はコード内のコメントに表示されます。
// 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」(不良!)です。
したがって、行の直前にprintステートメントを実行していないにもかかわらず、return 2;
行の直前でprintステートメントを実行したにもかかわらず、関数は-999を返しています。
ここで何が起こっているのか理解してくれる人がいますか?私は、彼らが初期化している場合を除き、Cの関数内の変数は、理論的には任意の古いがらくたを含めることができることを理解し、しかし、ここで私は、具体的...まだこれが起こっていないことをprint文のチェックイン、およびてる
編集:すべての人におかげで、特に彼らの偉大な答えのためのアンダースコアに感謝します。未定義の振る舞いのように見えますが、実際にはコンパイラはi
を含む何もスキップしません。コメントアウトここdetectSemicolon内serial.printsとアセンブリの一部です:
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を有効にして、関数が呼び出されるようにもう一度見ていきますが、これは私が思う強いポインターです。
EDIT 2:
あなたが見れば:本当に気に人のために
は、ここ(UBの場合)上図のとおりに逆アセンブルコードへのリンクです慎重に、コンパイラは、レジスタ012を、i
の位置として指定しており、d8
の0に "初期化"しているようです。このレジスタはwhileループ、if文などでは全体としてi
が含まれているかのように扱われ、コードが正常に動作し、printステートメントが期待通りに出力されます(たとえば、行122では "i"がインクリメントされます)。
しかし、この擬似変数を返すことについては、私たちの試行錯誤したコンパイラにとってあまりにも遠い段階です。それはラインを描画し、他のreturn文にダンプします(ライン120はライン132にジャンプし、レジスタ24と25に "-999"をロードしてからmain()
に戻ります)。
少なくとも、それは私の限られたアセンブリの把握で得ることができる限りです。コードの振る舞いが定義されていないとき、物語の道徳は奇妙なことが起こります。
これだけの機能から1つの戻りのポイントを持っていることをお勧めします。コードをリファクタリングすることをお勧めします。また、初期化されていない変数を決して使用しないことを指摘している人もいます。それが使用される前に他の場所で初期化されることが確実であれば、宣言されている場所を必ず初期化する必要はありません。 –
@RealtimeRik私は、多くの人が複数の「返品」ポイントについて不平を言うのを聞いたことがありません。これをアドバイスしている信頼できるエキスパートが記事を指すことができますか?私は、ここを出るのがはるかにクリーンな方法のように思えます。代わりに何をお勧めしますか? 'goto'? –
Err、not a goto。彼らはそこに使用しているが、それほど遠くない。私はシングルリターンポイントにより、コードをもっとテスト可能にしています。私はあなたのためのいくつかの参照を検索しようとします。 –