5

mainスタックでは、関数のスタック フレームと呼ばれるメモリが予約されmainます。

関数を呼び出すとAdd、スタックの一番上にメモリが予約されます。Add関数スタック フレームでは、とabローカル ポインターでありc、合計を計算して参照を返す整数です。c関数のローカル変数ですAdd

関数のAdd実行が完了すると、スタック内のメモリ空間も割り当て解除されるため、mainwith pointerpでこのアドレスにアクセスしようとすると、アクセスしようとしているのは基本的に割り当て解除されたスペースです。コンパイラは警告を出しますが、それでも値 5 が正しく出力されるのはなぜですか?

それに対する答えは、マシンがメモリ空間の割り当てを解除しなかったということかもしれません。それ以上の機能がなかったため、メモリ空間が必要であると見なされなかったからです。しかし、別の関数を書くと、呼び出しスタック内の関数Helloのスペースを確実に解放する必要Addがありますが、プログラムはまだ出力します

Yay     5

ヒープのように、解放後にポインターを割り当てる必要があるためnullですか、それともアクセスできますか? ここにそのようなものが関係していますか?

/* void Hello()
   {
     printf("Yay");
   } */

int* Add(int *a,int *b)
{
  int c=*a+*b;
  return &c;
}

int main()
{
  int a=1,b=4;
  int *p=Add(&a,&b);
  // Hello();
  printf("\t%d",*p);
  return 0;
}
4

5 に答える 5

8

cがローカル変数であると仮定すると、関数呼び出しの後intにアクセスすると、期待される結果が出力されるか、予期しないことが行われる可能性があります。cundefined-behavior

必須ではありませCんが、通常はスタックを使用して実装されます。パフォーマンス上の理由から、関数が戻るとき、スタック領域は変更されません。5これが、値ieが表示される理由です1+4。しかし、これを当てにしてはいけません。


2 番目の関数を使用する場合、動作は未定義のままであるため、任意の出力を取得できます。実際には、2 番目の関数で別の変数を定義して使用すると、出力が変わる場合があります。

                       +-----------------+
                       .(local variables).
                       |'''''''''''''''''|
                       |                 |
+----------------+     . PRINTF FUNCTION .
|  c = 42        |     |                 |
|''''''''''''''''|     +-----------------+
|                |     |                 |
.  ADD FUNCTION  .     . HELLO  FUNCTION .
|                |     |                 |
+----------------+     +-----------------+
|  b = 4         |     |  b = 4          |
|''''''''''''''''|     |'''''''''''''''''|
|  a = 1         |     |  a = 1          |
|''''''''''''''''|     |'''''''''''''''''|
|                |     |                 |
.  MAIN FUNCTION .     .  MAIN  FUNCTION .
|                |     |                 |
+----------------+     +-----------------+

Add上の図では、関数と関数の内部にいるときにスタックがどのように見えるかを視覚的に表現しようとしましたHello。関数内Helloで予約されていたスタック メモリを台無しにしていないことがわかります。cAdd

Hello関数を次のように書き換えることで、これを確認できます。

void Hello()
{
  int i = 42;
  printf("Yay - %d\n", i);
}

42で印刷できますmain

于 2015-01-15T12:36:12.533 に答える
1

TL;DR 回答、未定義の動作です。上記の実行の出力を正当化することはできません。携帯電話番号や郵便番号が印刷されても驚かないでください。:-)

スコープの有効期限が切れた後、スタックの場所に何が起こるかは環境に依存します。必要でない場合、割り当てられたスタック領域cが再利用されない可能性があるため、適切な出力が想定されていますが、まだ定義されていません。いつか違う環境で、違う答えが見えるかもしれません。

Mothit Jain氏の回答は、これについて非常に詳細な説明を提供します。

于 2015-01-15T12:34:28.487 に答える
1

これを完全に理解するには、プロセッサのアセンブリを学習し、コードがどのようにコンパイルされるかを確認することをお勧めします。この時点で、関数からの戻りはメモリ解放関数を呼び出さず、レジスタを調整するだけであり、追加と印刷の間で「割り当て解除」スタックの値を変更するようなことは何もしなかったと言うと、助けになると思います。あなたは番号を取得します。返す関数を呼び出してみてください.. pfft i dunno 77 間に (ただし、ローカル変数に保存してください!)、別の結果を出力します。ただし、アセンブリを学び、適切なプログラマーになりましょう。

于 2015-01-15T12:39:32.967 に答える
0

以下のプログラムを実行すると、Hello関数で「スタック変数」を定義しなかったことが原因だと思います。

#include <stdio.h>

void Hello()
{
  int tt = 100;
  int tt1 = 5000;
  printf("Yay");
}

int* Add(int *a,int *b)
{
  int c=*a+*b;
  return &c;
}

int main()
{
  int a=1,b=4;
  int *p=Add(&a,&b);
  Hello();
  printf("\t%d",*p);
  return 0;
}

Linux では5000 、Mac では1と出力されますが、どちらも予想外の値だと思います。

于 2015-01-15T12:42:29.547 に答える