2009-05-27 6 views
1

私はコードはこのような何かを見てバグを発見しました:面白いスコープの問題、説明?

char *foo = malloc(SOME_NUM * sizeof(char)); 
if (!processReturnsTrueOrFalse(foo)) { 
    free(foo); 
    char *foo = malloc(SOME_NUM * sizeof(char)); 
    // More stuff, whatever 
} 

これはコンパイルが、それは私が同じ関数内の2つの変数を定義させて頂くことに奇妙だが、コンパイラがスコープ異なり、それらに表示されます。

この場合、内側のfooと外側のfooを区別するにはどうすればよいですか?コンパイラは、2回目の宣言の前にフリーで、私は外側のfooを解放しようとしていましたが、内側のfooを再宣言すると、エラーが発生しませんでした。

ありがとうございました。おそらくこれはかなり明白な初心者の質問です。

答えて

12

C++は、変数のためにあなたが{}を使用するたびに新しいスコープを定義します。この例をここで見てください。

 
hello 
world 
global 
hello 

新しいスコープを開き、囲みスコープ内の変数と同じ名前の変数を宣言するとき、あなたは変数を隠す:あなたはこれを実行すると、あなたが得る

const char *foo = "global"; 

int main(int argc, char* argv[]) 
{ 
    const char *foo = "hello"; 

    { 
     cout << foo << endl; 
     const char *foo = "world"; 
     cout << foo << endl; 
     cout << ::foo << endl; 
    } 
    cout << foo << endl; 
} 

あなたが現在の範囲にとどまっている限り、外側のスコープ。内部スコープを離れると、外部変数が再び表示されます。外部変数がglobabl変数の場合は、グローバル名前空間::fooでアクセスできます。外部変数がクラス変数の場合は、className::fooを使用できます。外部変数がローカル変数だけの場合、変数を宣言したスコープを隠すまで、外部変数はアクセスできません。

私はしばらくのうちにCを使用していないので、C99は異なる可能性がありますが、古いCでは隠れた名前にアクセスする方法がありません。

 
world 
hello 
+0

この回答はC++固有のものです。質問にはCとobjective-Cも付いています。 – Trent

+0

真。 Cのソリューションはありますか? Cソリューションは基本的にObjective-Cソリューションもカバーします。範囲を隠してしまえば、何かにアクセスすることは単に不可能なのでしょうか? – Sam

+2

Cソリューションはありません。 Cでは、囲みスコープから隠し変数にアクセスすることはできません。 – Eclipse

3

2番目の 'foo'のスコープは宣言で始まり、宣言されているブロックの終わりまで続きます。フリー(foo)を呼び出すと、2番目のfooにはまだ宣言されていない。

2番目の 'foo'を宣言した後、外側 'foo'にアクセスする方法はありません。あなたは基本的に名前を隠しています。

+0

私は多くのことを考えましたが、if文のブロック内でどのように区別できますか? – Sam

+1

外部変数を隠した後に外部変数にアクセスする方法はありません。解決策は、foo変数の1つに異なる名前を使用することです。このような名前を非表示にするのは悪い習慣ですが、ほとんど常にトラブルを引き起こします。それらの名前を変更することも簡単です。 – Eclipse

+1

私は同意する、ジョシュ。私がこのセグメントをコーディングしていたとき、それを隠すことは私の意図ではありませんでした。私はタイプミス(または脳のスリップ)をして、実際にそれを考えずに、自分のmallocの最初にそのchar *をつけました。それをまとめてチェックしたところ、すぐに問題が発生し始めました。速いGDBセッションでこの問題が明らかになりました。それが私が質問した理由です。 – Sam

3

これはエラーではありません。異なるスコープで同じ名前の変数を宣言することは可能です。

コンパイラは、その時点でプログラムの唯一のfooなので、フリー関数内のfooは外側の関数であることを知っています。

第二のfooの宣言の後には、外側fooがグローバルとして(任意の関数の外)で宣言されている場合、外側のfoo、(編集)にアクセスする

::foo = ... 

を使用する必要があります。

+0

それで、もし私がより深く入れ子にし始めるなら(私は知っている、決してこれをしない、しかしこの時点で質問は純粋に学術的です)、私はちょうどセミコロンの数を増やすでしょうか? – Sam

+0

:: fooが外部fooにアクセスするためのコンパイラ/言語はどれですか?私は標準のCまたはC++でそれに精通していません。客観的なCについてはわからないが、私はそれが許されているとは思わない。 – Trent

+0

これは、fooがグローバル変数である場合にのみ機能し、fooがローカル変数でない場合にのみ機能します。 – Eclipse

2

これは、スコープとシンボルテーブルのためです。

ここにある例は、通常のスコープよりも有線です(このような構造を使用しないことを強くお勧めします)。しかし、コンパイラは、新しい変数を宣言した時点でシンボルテーブルを更新してから、シンボルテーブルの参照が外側のスコープに落ちるまで説明します。

あなたはこの例でポインタの変化のアドレス見ることができるように:

void *var = NULL; 
{ 
    std::cout << "From outer scope: " << &var << std::endl; 

    void *var = NULL; 

    std::cout << "From inner scope: " << &var << std::endl; 
} 
5

例えば、内側の範囲内で同じ名前の変数を宣言するときに、他のポスターはあなたに、shadow変数を書いたように:あなたはこれを実行すると、あなたが得る

const char *foo = "global"; 

int main(int argc, char* argv[]) 
{ 
    const char *foo = "hello"; 

    { 
     char *foo = "world"; 
     printf("%s\n", foo); 
    } 
    printf("%s\n", foo); 
    return 0; 
} 

機能またはブロック。事前C99コンパイラで使用すると、途中で変数を宣言することはできませんので、私はいくつかのダミーブロックを作っ

int foo = 1;      // global 

int bar(void) { 

    printf ("%d", foo);   // print global 
    { 
     int foo = 2;    // global foo shadowed 
     printf ("%d", foo);  // print local 
     { 
      extern int foo;  // local foo shadowed 
      printf("%d", foo); // print global 
     }       // global foo shadowed 
     printf ("%d", foo);  // print local 
    }        // end of scope for local foo 
} 

でもアクセスへの道があるがCでグローバルな非静的変数を影付きブロック。

+0

+1クール、私はそれを考えていませんでした。もちろん、あなたはまだ両方のfoosに同時にアクセスすることはできません。 – Eclipse

+0

ええ、それは主要な問題です - グローバルでもローカルでも、両方ではありません。 – qrdl