簡體   English   中英

C程序棧

[英]C program stack

#include<stdio.h>

int* foo() {
  int a = 5;
  return &a;
}

void bar() {
  int a = 7;
}

void foobar() {
    int a = 97;
}

int main() {
    int* p = foo();
    bar();
    foobar();
    printf("%d", *p);

    return 0;
}

我無法理解這種行為背后的概念。 為什么輸出始終是foobar函數中局部變量a的值?

不能這樣做:

int* foo() {
  int a = 5;
  return &a;  /* variable "a" is out of scope once "foo()" returns */
}

這是“未定義的行為” 結果可能因環境而異,因編譯器而異,甚至可能一次又一次地運行。 但這始終是“垃圾”。

這是未定義的行為,因為您返回了指向局部變量的指針,該局部變量在其功能范圍之外無效。 因此,內存中的這一點似乎可以重復使用。 永遠不要返回指向局部變量的指針!

程序具有未定義的行為,因為指針p由函數foo的局部變量的地址初始化

int* p = foo();

退出函數后,局部變量被銷毀並且指針無效。

ptogram始終輸出函數foobar的局部變量的值的原因是,似乎這些函數在調用時使用相同的堆棧框架。 因此,它們的局部變量位於堆棧中的同一地址。

如果您將通過以下方式更改函數foo

int* foo() {
  static int a = 5;
  return &a;
}

那么程序將輸出具有靜態存儲持續時間的函數的局部變量的值。

調用函數時,編譯器提供代碼以准備堆棧,保存當前堆棧指針並為局部(自動)變量騰出空間。 這通常稱為功能序言 序言之后的堆棧布局或多或少:

+-----------------------------+
|      Parameters if any      |
+-----------------------------+
|      Return address         |
+-----------------------------+
| copy of ESP (stack pointer) |
+-----------------------------+
| Local variables begin here  |
+-----------------------------+
|   ...                       |

現在,如果您具有3個將開發相同布局的函數:

      foo()                 bar()                foobar()
+----------------+    +----------------+    +----------------+
| Return address |    | Return address |    | Return address |
+----------------+    +----------------+    +----------------+
|      ESP       |    |      ESP       |    |      ESP       |
+----------------+    +----------------+    +----------------+
|     int a      |    |     int a      |    |     int a      |
+----------------+    +----------------+    +----------------+
|      ...       |    |      ...       |    |      ...       |

如果在第一個函數foo()獲得了變量a的地址,則在調用bar()foobar()時將重用相同的地址。 在調用之后訪問a ,您會發現foobar()寫入的最后一個值。

如果您以這種方式更改功能:

#include<stdio.h>

int* foo() {
  int a = 5;
  return &a;
}

int* bar() {
  int a;
  int b = 7;
  return &b;
}

int* foobar() {
  int a;
  int b;
  int c = 97;
  return &c;
}

int main() {
    int* p1 = foo();
    int* p2 = bar();
    int* p3 = foobar();
    printf("%d %d %d", *p1, *p2, *p3);

    return 0;
}

令人驚訝的是,您將讀取所有值。 現在的情況是:

      foo()                 bar()                foobar()
+----------------+    +----------------+    +----------------+
| Return address |    | Return address |    | Return address |
+----------------+    +----------------+    +----------------+
|      ESP       |    |      ESP       |    |      ESP       |
+----------------+    +----------------+    +----------------+
|     int a      |    |     int a      |    |     int a      |
+----------------+    +----------------+    +----------------+
|      ...       |    |     int b      |    |     int b      |
+----------------+    +----------------+    +----------------+
|      ...       |    |      ...       |    |     int c      |
+----------------+    +----------------+    +----------------+
|      ...       |    |      ...       |    |      ...       |

順便說一句,這屬於未定義行為的大家族,最重要的是錯誤 自動變量的壽命僅限於其范圍,並且不得在外部使用。

無論如何,堆棧上值的行為通常是穩定的,因為內存管理器會保留堆棧頁的數據(這就是為什么將未初始化的局部變量用作隨機值沒有意義),但是在將來的體系結構設計中,MM可以丟棄未使用的內存並且不要保存它,從而實際上未定義這些存儲位置的內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM