C/C++記憶體儲存

來源:互聯網
上載者:User

 

      C/C++記憶體儲存問題是筆試中必須掌握的。先看下面的程式:

#include <stdio.h>
#include "string.h"
#include "malloc.h"

void Swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
}

int Get_Int(int a)
{
int i=1+a;
return i;
}

char* Get_Memory0()
{
char* p=(char*)malloc(sizeof(char)* 20);
strcpy(p,"hello world");
return p;
}

char* Get_Memory1()
{
char* p="hello world";
return p;
}


char* Get_Memory2()
{
char p[]="hello world";
return p;
}

void main()
{
int x=4,y=3;
Swap(x,y);
int z=x-y;
printf("z=%d\n",z);

z=Get_Int(z);
printf("z=%d\n",z);

char* c0=Get_Memory0();
printf("c0=%s\n",c0);

const char* c1=Get_Memory1();
printf("c1=%s\n",c1);

char* const c2=Get_Memory2();
printf("c2=%s\n",c2);
}

基礎知識

記憶體儲存:

在C/C++中,通常可以把記憶體理解成4個分區:棧、堆、全域/靜態儲存區和常量儲存區。

(1)棧:通常是用那些在編譯期間就能確定其儲存大小的變數的儲存區,用於在函數範圍內建立、在離開範圍後自動銷毀的變數的儲存區。通常是局部變數、函數參數等的儲存區。它的儲存空間是連續的,兩個緊挨著的定義的局部變數,它們的儲存空間是緊挨著的。棧的大小是有限的,通常Visual C++編譯器預設棧的大小是1M,所以不要定義int a[1000000]這樣的超大數組。

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

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

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

程式解答:

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

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

答案:

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=未知

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.