質問
奇妙な問題が発生しました。整数変数をprintfしようとしていますが、変数名を指定するのを忘れていました。つまり、
printf("%d");
の代わりに
printf("%d", integerName);
驚くべきことに、プログラムはコンパイルされ、出力があり、ランダムではありません。実際、そもそも最初に印刷したかった整数で、m-1です。
エラーのある printf
ステートメントは、プログラムが実行されている限り、一貫してm-1を出力します...つまり、ステートメントが読み取るように動作します
printf("%d", m-1);
この行動の背後にある理由を知っている人はいますか?コマンドラインオプションなしでg ++を使用しています。
#include <iostream>
#define maxN 100
#define ON 1
#define OFF 0
using namespace std;
void clearArray(int* array, int n);
int fillArray(int* array, int m, int n);
int main()
{
int n = -1, i, m;
int array[maxN];
int found;
scanf("%d", &n);
while(n!=0)
{
found=0;
m = 1;
while(found!=1)
{
if(m != 2 && m != 3 && m != 4 && m != 6 && m != 12)
{
clearArray(array, n);
if(fillArray(array, m, n) == 0)
{
found = 1;
}
}
m++;
}
printf("%d\n");
scanf("%d", &n);
}
return 0;
}
void clearArray(int* array, int n)
{
for(int i = 1; i <= n; i++)
array[i] = ON;
}
int fillArray(int* array, int m, int n)
{
int i = 1, j, offCounter = 0, incrementCounter;
while(offCounter != n)
{
if(*(array+i)==ON)
{
*(array+i) = OFF;
offCounter++;
}
else
{
j = 0;
while((*array+i+j)==OFF)
{
j++;
}
*(array+i+j) = OFF;
offCounter++;
}
if(*(array+13) == OFF && offCounter != n) return 1;
if(offCounter ==n) break;
incrementCounter = 0;
while(incrementCounter != m)
{
i++;
if(i > n) i = 1;
if(*(array+i) == ON) incrementCounter++;
}
}
return 0;
}
解決
「驚くほどプログラムがコンパイルする」と言います。実際、まったく驚くことではありません。キャンプ; C ++では、関数で変数の引数リストを使用できます。 printfの定義は次のようなものです:
int printf(char*, ...);
&quot; ...&quot;関数にオプションの引数が0個以上あることを示します。実際、Cにオプションの引数がある主な理由の1つは、printf&amp;をサポートすることです。 scanfファミリーの関数。
Cには、printf関数に関する特別な知識はありません。あなたの例では:
printf("%d");
コンパイラはフォーマット文字列を分析せず、整数引数が欠落していると判断しません。これは完全に正当なCコードです。引数が欠落しているという事実は、実行時にのみ現れる意味上の問題です。 printf関数は、引数が指定されていると想定し、スタック上で引数を探します。そこにあるものは何でも拾います。あなたの特別な場合には正しいことを印刷しているだけですが、これは例外です。一般に、ごみデータを取得します。この動作はコンパイラごとに異なり、使用するコンパイルオプションによっても異なります。コンパイラの最適化をオンにすると、おそらく異なる結果が得られます。
私の答えに対するコメントの1つで指摘されているように、一部のコンパイラには「lint」があります。誤ったprintf / scanf呼び出しを実際に検出できる機能など。これには、コンパイラーがフォーマット文字列を解析し、予想される追加の引数の数を決定する必要があります。これは非常に特殊なコンパイラの動作であり、一般的なケースではエラーを検出しません。つまり、独自の&quot; printf_better&quot;を作成する場合printfと同じシグネチャを持つ関数の場合、コンパイラは引数が欠落しているかどうかを検出しません。
他のヒント
次のようになります。
printf("%d", m);
ほとんどのシステムでは、文字列のアドレスがスタックにプッシュされ、整数として 'm'
が取得されます(int / short / charと仮定)。 printf
は基本的に 'int printf(const char *、...);'
として宣言されているため、警告はありません。 / p>
つまり、「何でもする」ので、そこに変数を入れると奇妙なことが起こります。 intより小さい整数型はintになります-そのようなことです。何も送信しなくても大丈夫です。
printf実装(または少なくとも「単純な」実装)では、 va_list
および va_arg
の使用法があります(名前は適合性に基づいて若干異なる場合があります)。これらは、実装が引数リストの「...」部分を歩くために使用するものです。ここでの問題は、型チェックがないことです。型チェックがないため、 printf
は、フォーマット文字列(&quot;%d&quot;)
を見て、実行する必要があると判断すると、実行スタックからランダムデータを取得します。次に 'int'
になります。
暗闇でのランダムなショットは、printfが2番目のパラメーターとして 'm-1'
を渡す直前に行った関数呼び出しと言うでしょうか?それは多くの可能性のうちの1つです-しかし、もしこれが事実だったら興味深いでしょう。 :)
がんばって。
ところで、最新のコンパイラ(GCCと思う?)には、この問題を検出するために有効にできる警告があります。リントもそうです。残念ながら、VCでは無料で取得する代わりに/ analyzeフラグを使用する必要があると思います。
スタックから整数を取得しました。
あなたはスタックを覗き込んでいます。オプティマイザーの値を変更すると、変更される場合があります。変数の宣言の順序を変更します(特に) m
。 m
をレジスタ変数にします。 m
をグローバル変数にします。
何が起こるか、いくつかのバリエーションが表示されます。
これは、単純なI / Oを実行したときに発生する有名なバッファオーバーランハックに似ています。
これによりメモリ違反が発生することを非常に疑いますが、取得する整数は未定義のガベージです。
one の動作が見つかりました。無効なメモリアクセスを含む、他の動作である可能性があります。