15

ほとんどの管理対象言語(つまり、GCを使用する言語)では、スコープ外になるローカル変数にアクセスできず、GC優先度が高くなります(したがって、最初に解放されます)。

さて、Cは管理された言語ではありませんが、ここでスコープ外になる変数はどうなりますか?

Cで小さなテストケースを作成しました。

#include <stdio.h>
int main(void){
    int *ptr;

    {
        // New scope
        int tmp = 17;
        ptr = &tmp; // Just to see if the memory is cleared
    }

    //printf("tmp = %d", tmp); // Compile-time error (as expected)
    printf("ptr = %d\n", *ptr);

    return 0;
}

GCC 4.7.3を使用してコンパイルし、上記のプログラムが出力する17のはなぜですか?そして、いつ/どのような状況でローカル変数が解放されますか?

4

3 に答える 3

19

コードサンプルの実際の動作は、2つの主要な要因によって決定されます。1)動作は言語によって定義されていません。2)最適化コンパイラは、Cコードと物理的に一致しないマシンコードを生成します。

たとえば、動作が定義されていないという事実にもかかわらず、GCCはコードを単なるものに簡単に最適化できます(そしてそうするでしょう)。

printf("ptr = %d\n", 17);

つまり、表示される出力は、コード内の変数に何が起こるかとはほとんど関係がありません。

コードの動作に物理的に発生することをより適切に反映させたい場合は、ポインターを宣言する必要がありますvolatile。動作はまだ定義されていませんが、少なくともいくつかの最適化を制限します。

ここで、ローカル変数がスコープ外になるとどうなるかについて説明します。物理的なことは何も起こりません。一般的な実装では、プログラムスタックに十分なスペースを割り当てて、現在の関数にネストされているブロックの最も深いレベルにすべての変数を格納します。このスペースは通常、関数の起動時にスタックに一度に割り当てられ、関数の終了時に解放されます。

つまり、以前に占有されていたメモリはtmp、関数が終了するまでスタックに予約されたままになります。これは、同じスタックスペースが、兄弟ブロック内の「局所性の深さ」のレベルがほぼ同じであるさまざまな変数によって再利用できる(そして再利用される)ことも意味します。スペースは、兄弟ブロック変数で宣言された他の変数がオーバーライドするまで、最後の変数の値を保持します。あなたの例では、以前に占有されていたスペースを上書きする人はいないtmpため、通常、値17はそのメモリ内でそのまま存続します。

ただし、これを行うと

int main(void) {
  volatile int *ptr;
  volatile int *ptrd;

  { // Block
    int tmp = 17;
    ptr = &tmp; // Just to see if the memory is cleared
  }

  { // Sibling block
    int d = 5;
    ptrd = &d;
  }

  printf("ptr = %d %d\n", *ptr, *ptrd);
  printf("%p %p\n", ptr, ptrd);
}

以前に占有されtmpていたスペースが再利用されd、以前の値が上書きされていることがわかります。2つ目printfは通常、両方のポインターに対して同じポインター値を出力します。

于 2012-12-15T00:57:36.067 に答える
5

自動オブジェクトの存続期間は、それが宣言されたブロックの終わりで終了します。

存続期間外にオブジェクトにアクセスすることは、Cでは未定義の動作です。

(C99、6.2.4p2)「オブジェクトがその存続期間外に参照された場合、動作は未定義です。ポインターが指すオブジェクトがその存続期間の終わりに達すると、ポインターの値は不確定になります。」

于 2012-12-15T00:55:28.477 に答える
3

ローカル変数はスタックに割り当てられます。GC言語、またはヒープに割り当てられたメモリについて考えるという意味では、これらは「解放」されません。それらは単にスコープ外になり、組み込み型の場合、コードは何もしません-オブジェクトの場合、デストラクタが呼び出されます。

それらの範囲を超えてそれらにアクセスすることは未定義動作です。他のコードがそのメモリ領域を上書きしていないので、あなたはただ幸運でした...まだ。

于 2012-12-15T00:57:26.343 に答える