質問

奇妙な問題が発生しました。整数変数を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フラグを使用する必要があると思います。

スタックから整数を取得しました。

http://en.wikipedia.org/wiki/X86_calling_conventions

あなたはスタックを覗き込んでいます。オプティマイザーの値を変更すると、変更される場合があります。変数の宣言の順序を変更します(特に) m m をレジスタ変数にします。 m をグローバル変数にします。

何が起こるか、いくつかのバリエーションが表示されます。

これは、単純なI / Oを実行したときに発生する有名なバッファオーバーランハックに似ています。

これによりメモリ違反が発生することを非常に疑いますが、取得する整数は未定義のガベージです。

one の動作が見つかりました。無効なメモリアクセスを含む、他の動作である可能性があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top