コードサンプルの実際の動作は、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
は通常、両方のポインターに対して同じポインター値を出力します。