2
#include<stdio.h>

int* f1() { 
    int k =3; 
    return &k;
}

int main() { 
     int *z = f1(); 
     printf("%d",*z); 
}

OUTPUT は 3 です。 f1() のスタック アンバインドが発生するタイミングを知りたいです。

4

4 に答える 4

7

関数 f1() は変数 k のアドレスを返しますが、k はスタック上にあるため、括弧の後に変数 k をスタック メモリからアンワインドする必要があります。

そして、これはまさにコードが行うことです。しかし、あなたの例では、それを他の何かに使用する時間がないため、値3はアドレスに残ります。コンパイラは、このようなプログラムでスコープ外に出たことをテストできるようにするためだけに、スコープ外に出た変数を上書きするコードを生成しません。

あなたのプログラムは未定義の動作を呼び出します。したがって、生成されたコードの動作は、表示を含めて許容されます3


小さな C の例の定義に興味がある場合は、無料で入手できるこの静的アナライザーを使用して、プログラムについて次のように説明します。

$ frama-c -val t.c
…
t.c:3:[value] warning: locals {k} escaping the scope of f1 through \result
…
t.c:8:[kernel] warning: accessing left-value z that contains escaping addresses

最初の警告は単なる情報です。アナライザーは、関数がローカル変数のアドレスをそのスコープから外しても問題ないと見なします。結果として返されたアドレスの場合、それを直接プログラミング エラーと見なすべきではないかどうかは議論の余地があります。しかし、このエラーを深刻なものと見なさず、グローバル変数に書き込まれたアドレスについては、有益なメッセージのみを発行することは理にかなっています。プログラムがその後、グローバル変数に格納されたアドレスを使用することを回避する場合、この方法は安全です。

2 番目の警告は、不確定なアドレスzが使用されているため、未定義の動作を示しています。

于 2013-07-07T13:07:56.253 に答える
1

stackunwindunbindなどの用語に関する概念はすべて忘れてください。C 言語に関して言えば、このプログラムは未定義の動作を呼び出します。これは、自動ストレージのオブジェクト(別名「ローカル変数」)へのポインターが、オブジェクトの有効期間が終了した後に使用されているためです (}外側のブロックの関数のクローズ}。)

于 2013-07-07T13:19:11.793 に答える
1

スタックのバインド解除の意味がよくわかりません。要点は、 を呼び出したときに への呼び出しのスタック フレームがf1()有効でなくなったprintf()としても、上書きされる可能性は低いため、プログラムは (誤って) 期待される結果を返すということです。

実験を行い、次のようにプログラムを変更できます。

#include<stdio.h>

int* f1() { 
  int k =3; 
  return &k;
}

int* f2() { 
  int k = 4; 
  return &k;
}

int main() { 
  int *z = f1(); 
  int *y = f2(); 
  printf("%d %d",*z, *y); 
}

何が起こるのですか?どのような動作が見られても、それはコンパイラの動作方法にすぎず、別のコンパイラが同じ動作をするという保証はないことに注意してください。

于 2013-07-07T13:07:06.960 に答える
0

The generated machine code might look as follows: On returning from the function, the head-of-stack pointer is decremented (or incremented, if the stack grows from high to low addresses) by the size of variables and other information required by f1. Everything required by the calling function (main) has been allocated already, so no additional space on the stack is required, so the memory where k lived has not been overwritten when you dereference the return value delivered by f1. Hence, dereferencing still gives you the value 3. The address you access is, however, not part of the stack anymore.

stack within f1                            after returning from f1

       | ...            |                  | ...            |
SP---> | 3              | k                | 3              |
       | return address |                  | return address |
       | ...            |             SP-->| ...            |

SP points to the top of the stack. Returning from the function probably simply changes SP. Everything above SP can be considered as unused, but probably is not overwritten by e.g. zeros, so it may contain "old" content.

于 2013-07-07T13:11:42.970 に答える