2
int bar = 2;
if (bar)
{
   int bar;
}

gcc も Clang もこれに対して警告 (またはエラー) を発行できず、プログラムは起動時にすぐにクラッシュします。これには正当な理由がありますか?なかなか釣れそうにありません。これはブロックスコープの基本です。ネストされたスコープは、囲んでいるブロックの名前を継承します...

説明はありますか?

編集:クラッシュはClangの使用が原因であることが判明しました。私は何度も何度もテストしましたが、変数の再定義と Clang の組み合わせがクラッシュを引き起こすことは確かです。ただし、テスト プロジェクトでクラッシュを再現できなかったので、考えてみてください。

この問題は、Objective-C 関連であることが判明しました。Jonathan Leffler が指摘しているように、内部スコープで「int bar = bar」を実行すると、それ自体から変数が初期化されます。これが問題の原因です。初期化が Objective-C メソッド呼び出しを介して行われる場合です。

以下は、動作中のバグを示しています。

-(void)crasher
{
   NSNumber* bar = [NSNumber numberWithInt:2];
   if (bar)
   {
      NSString* bar = [self doit:bar];
   }
}

-(NSString*)doit:(NSNumber*)num
{
   NSString* str = [num stringValue];   // This line causes the crash
   return str;
}

純粋な C で同様のことを行ってもクラッシュしないことに注意してください。

int bar = 2;
if (bar)
{
   char buff[10];
   int bar = sprintf(buff, "%d",bar);       
}
4

5 に答える 5

7

ここでキャッチするものは何もありません。内側のブロックの変数は完全に異なる変数であり、外側のブロックの変数を隠しています。これは言語の完全に標準的な機能であり、時代の初めから存在しています。

発生しているクラッシュは、投稿したコードとはまったく関係ありません。コードを間違えない限り、外側の変数を操作していると仮定して、内側の変数を操作します。

于 2010-03-05T17:04:59.543 に答える
4

これは、ネストされたスコープの基本です。ネストされたスコープ内では、外側のスコープで宣言されたものをシャドウできます。gcc には、これに対する警告を取得するオプション (-Wshadow) がありますが、-Wall でも -Wextra でもアクティブ化されません。警告は、コードを変更せずに表示できます (ヘッダーには、グローバル スコープでの定義があります。関数で使用される識別子)。

于 2010-03-05T17:09:14.510 に答える
1

Douglas Leeder による回答の拡張:

#include <stdio.h>
static int xx(int foo)
{
    int bar = 2;
    if (foo > bar)
    {
        int foo = bar;
        int bar = bar;
        printf("inner: foo = %d, bar = %d\n", foo, bar);
    }
    printf("outer: foo = %d, bar = %d\n", foo, bar);
    return bar;
}
int main(void)
{
    xx(13);
    return(0);
}

内側のバーはそれ自体から初期化されることに注意してください。これにより、未定義の動作が発生します。しかし、MacOS X 10.6.2 (GCC 4.2.1) では次のようになります。

inner: foo = 2, bar = 0
outer: foo = 13, bar = 2

バリエーション 1: スタック トランプル - A

興味深いことに、このコードから、スタック トランプリング関数を使用して同じ出力が得られiますa

inner: foo = 2, bar = 20
outer: foo = 13, bar = 2

コード:

#include <stdio.h>
static void modify_stack(void)
{
    int a[20];
    int i;
    for (i = 0; i < 20; i++)
    {
        a[i] = 0xFFFFFFFF ^ i;
        printf("a[i] = 0x%08X\n", a[i]);
    }
}
static int xx(int foo)
{
    int bar = 2;
    if (foo > bar)
    {
        int foo = bar;
        int bar = bar;
        printf("inner: foo = %d, bar = %d\n", foo, bar);
    }
    printf("outer: foo = %d, bar = %d\n", foo, bar);
    return bar;
}
int main(void)
{
    modify_stack();
    xx(13);
    return(0);
}

動作は未定義なので、この結果は問題ありません。

バリエーション 2: スタック トランプル - B

#include <stdio.h>
static int modify_stack(void)
{
    int a[20];
    int i;
    for (i = 0; i < 20; i++)
    {
        a[i] = 0xFFFFFFFF ^ i;
        printf("a[i] = 0x%08X\n", a[i]);
    }
    i = a[13];
    return(i);
}
static int xx(int foo)
{
    int bar = 2;
    if (foo > bar)
    {
        int foo = bar;
        int bar = bar;
        printf("inner: foo = %d, bar = %d\n", foo, bar);
    }
    printf("outer: foo = %d, bar = %d\n", foo, bar);
    return bar;
}
int main(void)
{
    int i = modify_stack();
    xx(13);
    return(i & 0xFF);
}

出力 (ループで印刷されたデータを除く):

inner: foo = 2, bar = -14
outer: foo = 13, bar = 2
于 2010-03-05T17:33:27.817 に答える
1
$ gcc 1.c

$ gcc -Wall 1.c
1.c: In function ‘main’:
1.c:6: warning: unused variable ‘bar’

$ cat 1.c
int main()
{
    int bar = 2;
    if (bar)
    {
        int bar;
    }
    return 0;
}

$ ./a.out ; echo $?
0

私のためにコンパイルします--Wallの下に警告があります。そして、プログラムは正常に実行されます。

2 つの変数を宣言しました。1 つは呼び出されbar、もう 1 つは も呼び出されbarます。スコープが異なる限り、コンパイラは気にしません。

于 2010-03-05T17:06:58.633 に答える
1

内側のスコープで外側のスコープから名前を再定義することはエラーではなく、通常のことであり、予期されることです。そうしないと、基本的に、グローバル変数しか持たない古い BASIC 方言の古い時代に戻ることになります。

于 2010-03-05T17:07:19.827 に答える