C 記憶體儲存,引數傳遞的深度理解

2021-07-12 02:57:25 字數 2985 閱讀 2375

#include #include "string.h"

#include "malloc.h"

void swap(int a,int b)

int get_int(int a)

char* get_memory0()

char* get_memory1()

char* get_memory2()

void main()

記憶體儲存:

在c/c++中,通常可以把記憶體理解成5個分割槽:棧、堆、全域性/靜態儲存區和文字常量區、程式**區。

(1)棧:通常是用那些在編譯期間就能確定其儲存大小的變數的儲存區,用於在函式作用域內建立、在離開作用域後自動銷毀的變數的儲存區。通常是區域性變數、函式引數等的儲存區。它的儲存空間是連續的,兩個緊挨著的定義的區域性變數,它們的儲存空間是緊挨著的。棧的大小是有限的,通常visual c++編譯器預設棧的大小是1m,所以不要定義int a[1000000]這樣的超大陣列。

(2)堆:通常是用於那些在編譯期間不能確定儲存大小的變數儲存區,它的儲存空間是不連續的,一般同malloc(或new)函式來分配記憶體塊,並且需要要free(或delete)釋放記憶體。如果程式設計師沒有釋放掉,那麼就會出現常說的記憶體洩漏問題。需要注意的是,兩個緊挨定義的指標變數,所指向的malloc出來的記憶體並不一定是緊挨著的。另外需要注意的一點是,堆的大小幾乎是不受限制的,理論上每個程式最大可達4gb。

(3)全域性/靜態儲存區:和「棧」一樣,通常是用於那些在編譯期間就能確定儲存大小的變數的儲存區,但它用於的是在整個程式執行期間都可見的全域性變數和靜態變數。

(4)常量儲存區:和「全域性/靜態儲存區」一樣,通常是用於那些在編譯期間就能確定儲存大小的常量儲存區,並且在程式執行期間,儲存區內的常量也是全域性可見的。這是一塊比較特殊的儲存區,它們裡面放的是常量,不允許被修改。

(5)程式**區—存放函式體的二進位制**。

程式解答:

(1)swap函式:我們知道,所有函式都會在執行時從程式「棧」上得到分配給它的一塊儲存區(這裡的「棧」就是前兩面講解記憶體儲存時提到的「棧」,第個執行程式都擁有自己的執行時「棧」)。這塊「棧」上的函式儲存區隨函式的開始面開始,隨著函式的結束而結束。函式結束後,這塊儲存區就會自動釋放,以供程式的其他用途使用。系統在函式執行時會為函式的每乙個引數都提供儲存區,引數在儲存區中的儲存長度由自身的型別決定。引數傳遞,就是系統用函式實參初始化函式引數儲存區的過程。

函式的引數有傳值(傳指標)和傳址(傳引用)兩種。所謂參會傳值,就是實參的值複製到函式執行時分配給函式的引數儲存區中。引數在傳值時,函式不會訪問當前呼叫的實參,函式處理的是實參在本地的拷貝,這些拷貝被儲存在函式的「棧」中,所以這些拷貝值的改變不會影響實參的值。

答案:z=1

(2)問題:返回值i是乙個區域性變數,函式的返回引數怎麼能是區域性變數呢?區域性變數在離開作用域後就自動被銷毀了,還怎麼能返回值給呼叫都呢?

解答:函式的返回值有傳值和傳址兩種。int get_int(int a)屬於返回值是傳值的函式,這就意味著函式int get_int(int a)會在函式返回處產生乙個臨時物件,用於存放區域性變數i的值的乙份拷貝(變數i的右值的拷貝),臨時物件是沒有名稱的,這份沒有名稱的物件的值(右值)會儲存在呼叫者的「棧」中。所以當i作為區域性變數離開作用域後,雖然被銷毀了,但它的拷貝仍然存在,並在函式返回時作為「右值」賦給「左值z」。

答案:z=2

(3)此處考查對「堆」的理解:函式返回的是指向「堆」記憶體的指標。程式中malloc()是用於分配「堆」記憶體的庫函式,而對於「堆」記憶體,只要程式中沒有呼叫free()庫函式去釋放掉該「堆」記憶體,那麼在程式執行期間,malloc()庫函式分配的「堆」記憶體將一直存在。

char* p=(char*)malloc(sizeof(char)*20)表示分配一塊「堆」記憶體並使得變數p指向這塊「堆」記憶體。

strcpy(p,"hello world")表示往p指向的「堆」記憶體中複製字串hello world。

return p,變數p的左值是乙個區域性變數指標,儲存於函式棧上,右值 是「堆」的位址(「堆」的值是hello world)。

函式char* get_memory0()屬於返回值是傳指標的函式,這就意味著函式char* get_memory0()會在函式返回處產生乙個對返回變數p的「左值」的拷貝,也就是在「左值」的拷貝中儲存了指向「堆」的位址。作為區域性變數的 p,在離開函式作用域的時候雖然被銷毀了,但函式返回值珠「左值」的拷貝是存在的,該拷貝儲存了指向「堆「的位址,而而該「堆」的值是hello world。

答案:c1=hello world

(4)這裡考查對函式返回值和指標的理解:變數分為左值和右值,在char* p="hello world"中,左值是區域性變數指標p,儲存於函式棧上,右值是字串常量hello world,儲存於常量儲存區。

char* get_memory1()屬於返回值是傳指標的函式,這就意味著函式char* get_memory1()會在函式返回處產生乙個對返回物件的「左值」的拷貝,也就是在「左值」拷貝中儲存了指向字串常量hello world的位址。作為區域性變數的p,在離開函式作用域的時候雖然被銷毀了,但函式 返回的「左值」拷貝仍然儲存了指向常量儲存區的字串常量hello world的位址。

答案:c1=hello world

(5)和問題(4)有些相似,考查的也是對函式返回值和指標的理解:返回值是傳指標,會在函式返回時產生「左值」拷貝。可以看到,p不是指標,是乙個陣列變數。編譯器根據右值「hello world」的長度(12個字元)在編譯期間在函式的棧上為陣列分配大小為12個字元的記憶體儲存區,其值是「hello world」。

通常,變數的意義在於,它給一塊記憶體儲存區提供名字,方便程式對這塊記憶體進行讀寫。變數包含兩個值:左值和右值。左值是記憶體儲存區的名字,右值是存放儲存區中的值。從程式中可以看到,函式返回的「左值」拷貝 指向的是區域性變數陣列p[12]的首位址。當區域性陣列p[12]作用域後會被自動銷毀。這時,函式返回的「左值」拷貝指向的是乙個被銷毀的區域性變數位址。

答案:warning c4172: returning address of local variable or temporary

c2=未知

c 指標引數是如何傳遞記憶體的

引數策略 如果函式的引數是乙個指標,不要指望用該指標去動態申請記憶體。如下 void getmemory char p,int num void test void 原因是編譯器總是為每個引數製作臨時副本。指標引數p,其副本為 p,使 p p。如果改變了 p所指的內容,相應的p所指的內容也跟著改變 ...

c 指標引數是如何傳遞記憶體的

void getmemory char p,int num void test void void getmemory char p,int num void test void 原理是一樣的,比較難理解,圖示表示 比較好的方法是 傳指標的引用 include include include inc...

c 指標引數是如何傳遞記憶體的

引數策略 如果函式的引數是乙個指標,不要指望用該指標去動態申請記憶體。如下 void getmemory char p,int num void test void 原因是編譯器總是為每個引數製作臨時副本。指標引數p,其副本為 p,使 p p。如果改變了 p所指的內容,相應的p所指的內容也跟著改變 ...