Cで数週間遊んだ後、今では嫌いです。
これは珍しい反応ではありません。私の CS 入門クラスの 3 分の 1 は、 C の難しさを理由に専攻を変更しました (C は恐ろしい教育言語です)。頭を十分に理解するのに数年かかりましたが、一度理解すると感謝するようになりました。
char str_w,str_r;
snprintf(&str_w, 2, "%d", select[1]);
snprintf(&str_r, 2, "%d", result[0]);
これが主な問題です。str_w
文字列ではなくstr_r
、単一のchar
値を保持するのに十分な大きさです (少なくとも2 文字が必要です)。代わりに、予想される最大の文字列表現、符号用のスペース (値が負の場合)、および 0 ターミネータを保持するのに十分な大きさのstr_w
との配列str_r
として宣言する必要があります。たとえば、またはの値を制限していなかった場合:char
int
select
result
#define MAX_DIGITS 20 // max decimal digits for 64-bit integer
#define SIZE MAX_DIGITS+2 // 2 extra for sign and 0 terminator
char str_w[SIZE], str_r[SIZE];
sprintf(str_w, "%d", select[1]);
sprintf(str_r, "%d", result[0]);
可能な入力に対して十分な大きさのターゲット配列を作成することで、オーバーフローを心配する必要がなくなります。はい、内部の断片化が少し発生します。アプリケーションによっては、問題になる場合とそうでない場合があります。しかし、私は物事をシンプルに保つのが好きです。
および配列が 0..10 の範囲外の値を保持しないことがわかっている場合は、SIZE を 3 (最大 2 桁と 0 ターミネータ) に設定 できます。select
result
つまり、select[1] には数値 (5) があり、result[0] には数値 (6) があり、result[0] は適切に文字列に変換されますが、select[1] はありません。
何だその行為は!!
結果を保持するのに十分な大きさではないバッファにアドレスを渡したので、動作はundefinedです。つまり、コンパイラは、危険なことをしていることを警告する義務はありません。
これが最も起こりそうなことです (動作は定義されていないため、イベントの順序は任意ですが、これは結果の合理的な解釈だと思います)。まず、変数が次のようにメモリに配置されているとします。
Item Address Value
---- ------- -----
str_r 0xffec1230 ??
str_w 0xffec1231 ??
0xffec1232 ??
str_w
とstr_r
連続するバイトに割り当てられ、初期値は不定です。最初のsnprintf
~の後str_w
、メモリは次のようになります。
Item Address Value
---- ------- -----
str_r 0xffec1230 ??
str_w 0xffec1231 '5'
0xffec1232 0
snprintf
末尾の 0 ターミネータをバッファに書き込みます。この場合、ターミネータを次のバイトに書き込みますstr_w
。2 回目のsprintf
呼び出しの後、メモリは次のようになります。
Item Address Value
---- ------- -----
str_r 0xffec1230 '6'
str_w 0xffec1231 0
0xffec1232 0
2 番目の呼び出しでは、たまたま;の後にsnprintf
続くバイトに 0 ターミネータが書き込まれました。以前に書き込まれた値を壊してしまいました。そのため、文字列は表示されますが、文字列は表示されません。 str_r
str_w
str_r
str_w