C語言,局部變量的指針,棧幀

2020-12-06 閒聊代碼

C/C++不能返回局部變量的指針,是一條重要的語法規則。

至於為什麼,則不是那麼顯眼。

局部變量,是分配在棧上的變量,隨著函數調用的返回而失效。

函數調用結束之後,局部變量的指針,也就是野指針了,不能在函數外繼續使用。

函數調用時的棧,也叫棧幀。它在函數開始時的棧頂,就是調用完成之後的返回地址。

局部變量,在這個基礎上分配。

函數的實參,也是局部變量。不過一般放在寄存器裡,或者比返回地址更高的地址上。

被調函數開始時,x64平臺的棧幀:

實參7

實參6

RET <- rsp指向這裡

實參0-5在寄存器裡,前6個參數通過寄存器傳遞,超過6個的參數用棧傳遞,返回地址(RET)棧頂

棧頂,保存在rsp寄存器裡。

為了函數返回和查找局部變量(包括實參)的方便,x64提供了rbp寄存器,用於在函數執行過程中保存棧的底部

如果是32位的x86,寄存器是esp、ebp。

所以函數的開頭代碼,在需要分配局部變量時,一般是:

push %rbp,

mov %rsp,%rbp

sub $size,%rsp

第一行把rbp的原值保存在棧上,

第二行把rsp的值保存到rbp裡,

第三行通過把rsp往低地址移動,從而分配局部變量的內存。

size表示所有局部變量的大小,要編譯器根據函數代碼計算出來。

這個時候的棧幀變成了:

實參7

實參6

RET

rbp原值 <- rbp的新值

局部變量0

局部變量1 <- rsp的新值

假設就2個局部變量,RET表示函數調用結束時的返回地址。

這時,rbp寄存器指向的是返回地址的下一個位置,即被調函數的棧低

這也是rbp的名字的由來:

r指的是register,寄存器,

b指的是bottom,底部,

p指的是pointer,指針,它本質上是指示函數棧的底部的指針。

rsp,指的自然是棧的頂部,s是stack。

因此,可以用rbp+對應的偏移量offset來訪問局部變量和實參。

rsp可能會改變,例如在調用子函數時,所以一般都是用rbp作為標準來訪問局部變量。

在函數結束時,要把棧幀恢復到開始之前,常用的代碼是:

mov %rbp,%rsp

pop %rbp

ret

第一行把棧底rbp覆蓋到棧頂rsp,這時的rsp指向了rbp的原值,局部變量的內存空間已經被清理

第二行,把保存在棧上的rbp原值恢復到rbp寄存器。這個值是被調函數剛開始時的rbp值。

因為要用rbp保存棧幀的底部,然後在此基礎上分配局部變量的內存,所有先保存rbp的原值,然後再分配,否則返回調用函數之後這個值就丟了。

第三行,這時候棧頂rsp指向返回地址,棧恢復到了被調函數開始時的樣子,可以用ret指令返回了。

局部變量既然失效了,它的指針自然就不能再用了。

使用野指針,那是要出BUG的。

高級語言的特點就是,它會告訴你什麼,但不一定會告訴你為什麼,但它還要你記住什麼:(

C這種大號的彙編也是這樣。

如果局部變量是個字符數組,那麼在字符串拷貝strcpy()時溢出了,就會覆蓋返回地址。

因為返回地址就在局部變量上面不遠。

覆蓋之後,程序就不按照程式設計師的思路跑了,而是按照提供這個字符串的人的思路跑了,這就是當年著名的字符串緩衝區溢出漏洞

最後搞得Linux關閉了堆棧的可執行權限,gcc加上了字符串溢出的檢查。

當年丹尼斯·裡奇為了節約內存,不記錄字符串的長度,而是拿字節0作為結束標誌導致的大BUG。

相關焦點

  • C語言的指針,簡介
    指針,是一個表示變量或函數的地址的無符號整數。指針的字節數,與CPU的位數有關,32位機是4位元組,64位機是8位元組。與高級語言的long類型的大小是一致的。所以在java的JNI庫裡,如果需要把native層的C結構體的指針保存到java層,一般是定義一個long類型的變量。指針指向的變量,可以是普通變量、數組變量、結構體變量,以及數組元素、結構體的成員變量,還可以是指針變量。指針還可以指向函數,叫做函數指針。
  • C語言陷阱與技巧31節,都說void*指針是「萬能指針」,它萬能在哪
    例如 int 型指針告訴編譯器該地址處緊接著的 4 字節按照 int 型數據解釋,double 型指針高速編譯器接下來的 8 字節按照 double 型數據解釋。這裡假定int型變量佔用4位元組內存空間,double 型變量佔用 8 字節內存空間。
  • 青少年信息學競賽Pascal語言:指針(十)
    合肥奧數網訊:合肥市青少年信息學競賽Pascal語言:指針   指針   指針是通過地址來訪問變量的一種特殊的數據類型,屬於動態的數據結構,它可以在需要時產生,用完後則又可以取消或回收,以減少佔用的內存空間。指針變量與其他類型的變量不同,它佔有的不是數據,而是地址。
  • 51單片機基礎剖析(基於C語言)
    全局變量(按變量的有效作用範圍劃分) 1.局部變量 是指函數內部(包括main函數)定義的變量,僅在定義它的那個函數範圍內有效,不同函數可使用相同的局部變量名,函數的形式參數也屬於局部變量,在一個函數的內部複合語句中也可以定義局部變量,該局部變量只在該複合語合中有效。
  • C語言編程:以實例教你學指向函數的指針
    指針是C語言的精髓,對於初學者來講,指針是C語言語法學習中比較難的知識點,而這裡面指向函數的指針更是不太容易理解。下面給大家講下怎樣學習理解C語言中指向函數的指針及編程方法和使用例子。注意:這是一篇關於C語言編程的基礎語法內容,C語言大神請繞過。
  • C語言|文件指針、fopen()、fscanf()、fclose()
    這是一個簡單的文件系統fscanf()函數的功能是把磁碟文件數據讀出保存到變量(內存)每一個文件都有自己的FILE結構和文件緩衝區exit(0)是系統標準函數,作用是關閉所有打開的文件,並終止程序的執行。
  • 第四篇:C語言中指針與字符串核心知識點梳理
    前面在講變量的時候,其中有一個特點就是變量的內存地址,即:變量在內存中實際的保存位置。這個內存地址如何獲取?它又有什麼意義?這就涉及到本文要講到的第一個核心概念:指針。重點包括:指針處理一維數組、動態內存分配等。C語言的基本數據類型中有一個char的關鍵詞,可以存儲單個的字符。那麼,像漢字以及由多個字符組成的內容,又該如何存儲呢?
  • C語言中的「不透明」指針是什麼,它有什麼用呢?
    雖說指針是C語言中比較複雜的語法,但是確實非常好用,因此我寫過不少文章討論C語言中的指針,相信對初學者理解指針有一定的幫助。事實上,的確有讀者私信我說看了這些文章,「總算不再畏懼指針了」。不過他同時也問了一個問題:C語言有「不透明指針(opaque pointer)」嗎?
  • 計算機二級考試C語言高頻考點
    (2)源程序的擴展名為.c,目標程序的擴展名為.obi,可執行程序的擴展名為.exe。C語言的標識符可以分為3類。①關鍵字:C語言規定的專用的標識符,它們有著固定的含義,不能更改②預定義標識符:和「關鍵字」一樣也有特定的變量要有變量名,在使用前必須先定義。③用戶標識符:由用戶根據需要定義的標識符。
  • 深入理解C語言
    導讀:Dennis Ritchie過世了,他發明了C語言,一個影響深遠並徹底改變世界的計算機語言。一門經歷40多年的到今天還長盛不訓的語言,今天很多語言都受到C的影響,C++,Java,C#,Perl,PHP,Javascript等等。但是,你對C了解嗎?相信你看過本站的《C語言的謎題》還有《誰說C語言很簡單?》。
  • ARM中ADS環境下C語言和彙編語言混合編程及示例
    ;  char b[64];  my_strcpy(a, b);  printf(original %s, a);  printf(copyed %s, b);  return 0;  }  在此例子中C語言和彙編之間的值傳遞是用C語言的指針來實現的,因為指針對應的是地址,所以彙編中也可以訪問。
  • C語言之const和volatile"究極"學習
    一、const的用法:1、const只讀變量:const修飾的變量是只讀的,本質上還是變量const修飾的局部變量在棧上分配空間const修飾的全局變量在全局數據區分配空間const只在編譯期有用,在運行期沒有用註:const修飾的變量不是真的常量,它只是告訴編譯器該變量不能出現在賦值符號的左邊
  • 「C語言從入門到入土」必備C語言基礎筆記整理
    一、C語言1、什麼是C語言?C語言是人寫機器看的一種語言。C語言是高級語言中的低級語言。C語言貼近硬體。C語言的入門學習比較簡單。表示問號)printf("輸出三個數 :\n%d\n%d\n%d\n",a,b,c); //變量要先定義,後使用。 在這裡使用換行時應注意 寫成格式 「%d\n」//三個數則要書寫三次。它們相連之間不用分號,也不用逗號。
  • C/C+編程筆記:C語言編程面試常見問題,全是經典題!
    C是一種過程語言。C語言的主要功能包括對內存的低級訪問,簡單的關鍵字集和簡潔的樣式。這些功能使其適用於諸如作業系統或編譯器開發之類的系統編程。 i ++和++ i有什麼區別? 1)表達式「 i ++」返回舊值,然後遞增i。
  • C語言基礎教學檔案!編號零零肆
    C語言令牌每個C程序都是一組指令,每個指令都是一些單獨的單元的集合。每個最小的單個AC程序單元稱為令牌。AC程序(用來定義電腦程式的形式語言)中的每條指令都是令牌的集合。令牌用於構建c程序,它們被稱為AC程序的基本構建塊。
  • 2003年10月甘肅省高等教育自學考試C語言程序設計試卷
    B2 C4 D5  12.設a為5,執行下列語句後,b的值不為2的是 ()  Ab=a/2 Bb=6-(-a)  Cb=a%2 Db=a>32:1  13.假設指針p1已經指向了某個整型變量,要使指針p2也指向同一個變  量,則下面各項中正確的是 ()  Ap2=**p1 Bp2=*&p1
  • c語言中malloc申請的空間和直接定義變量申請的空間有什麼區別?
    直接定義變量與malloc定義變量的編程含義;malloc事先分配好了內存空間。在c語言中,malloc函數原型為void*malloc(unsignedintslong),其作用是在內存的動態存儲區中分配一個長度為slong的連續空間。次函數的返回值是分配區域的起始地址,如:char*p;p=malloc(64000);//空間不夠指針為null。
  • C語言基礎知識學習(一)
    標識符在程序中使用的變量名、函數名、數組名、指針名、標號等稱為標識符.c) 用戶標識符根據需要定義的標識符。一般用來給變量、函數、數組、文件等命名。用戶標誌符如果與C語言的關鍵字重名,系統報錯;若與標準庫函數重名,系統不報錯,但預定義標識符將會失去原意,代之以用戶新定義的含義。顯然如果後面用到這個函數將會報錯。
  • 自考「C語言程序設計」模擬試題九
    A.d&ef          B.6a            C.z4x5c           D.a3/b4  3.在C語言中,存儲一個字符型、整型、單精度實型變量所需的空間是()。  int  x;   char  y;   char  z[20];  A. scanf(「%d%c%c」,&x,&y,&z);     B. scanf(「%d%c%s」,&x,&y,&z);  C. scanf(「%d%c%c」,&x,&y,z);
  • C語言相關文件的基本知識
    用過計算機的人,對文件都不陌生,大多數人都接觸過,今天我們來講講關於C語言相關文件的基本知識。首先,我們先來了解了解什麼是文件。文件有不同的類型,在程序的設計中,主要有兩種文件; 1.程序文件;包括源程序文件(後綴為.c),目標文件(後綴為.obj),可執行文件(後綴為.exe)等,這類文件的內容是程序代碼。