上接C/C++要點全掌握(一)
9、堆棧
棧(stack):為程序保存的一塊內存區域,用來保存狀態數據,其訪問順序是後進先出(LIFO)。例如:過程或函數的地址,參數,有時候還包括局部變量。
堆(heap):為程序保存的一塊內存區域,用來保存那些在運行時才知道其存在與大小的數據;程序能夠從堆中分配內存給這些元素;在用完之後,應該釋放掉這些內存。堆中所有的的東西都是匿名的,不能按名字直接訪問只能通過指針間接的訪問。
所以,堆和棧的區別:一個是動態,一個是靜態; 堆是在程序運行時動態分配的,而棧的分配是編譯完後,就確定了;棧內存的回收,系統自動進行了,而堆上分配的內存,需要手工顯式回收。
malloc , new分配的內存就是在堆上的,如果不用free, delete回收,就會造成內存洩漏(垃圾),不過,一般操作系統會在整個程序(進程)退出時,回收分配給這個進程的內存等資源。
(以下內容摘自百度百科堆棧)
一個由C/C++編譯的程序占用的內存分為以下幾個部分。
(1)棧區(stack):由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
(2)堆區(heap):由程序員分配釋放, 若程序員不釋放,程序結束時可能由操作系統回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表。
(3)全局/靜態區(static): 全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束後由系統釋放。
(4)文字常量區:常量字符串就是放在這裡的,程序結束後由系統釋放。
(5)程序代碼區:存放函數體的二進制代碼。
這是一個前輩高人寫的,非常詳細。
int a = 0;// 全局初始化區
char *p1;//全局未初始化區
void main()
{
int b; //棧
char s[] = "abc"; //棧,運行時賦值
char *p2;// 棧
char *p3 = "123456";// 123456\0在常量區,p3在棧上。編譯時確定。
static int c =0;// 全局(靜態)初始化區
p1 = (char *)malloc(10); //p1指向堆區,p1在棧上
}
關於堆棧的其他信息參見百度百科有關堆棧的介紹。
10、指針辨析
int * p;//指向int型的指針
void * p;//空類型指針
int * arr[10];//指針數組x存放10個指向int型的指針
int** pp;//指針的指針(指向int型的指針的指針)
int (*func_p)(int,int);//函數指針
注:在C中可以直接將void*指針賦值給其他任意類型指針,而在C++中需使用強制類型轉換。
指針使用的典型錯誤
//函數功能:對指定的字符指針分配內存。
void func(char* p)
{
p=malloc(100);
}
void main()
{
char* str=NULL;
func(str);//給str分配內存
...
free(str);//用完釋放內存
}
調用函數func(str)貌似給str分配了內存,其實不然。在此形式參數p是一個局部指針變量,調用語句首先用str(在此即NULL,也可以是一個char型數據的內存地址)初始化p,使p和str指向同一位置,之後申請內存空間並將首地址賦給p,此時p與str指向已不同,之後局部變量p被銷毀。整個過程根本就沒有修改str的指向位置,更談不上為str分配內存了。
通過此例可知並不是形參為指針類型就可以改變實參的值。要達到“引用傳參”的效果必須在函數內給*p賦值。結合上例修改後代碼如下:
//使用指針的指針傳參
void func(char** p)
{
*p=malloc(100);
}
void main()
{
char* str=NULL;
func(&str);// 給str分配內存
...
free(str);//用完釋放內存
}
針對上例,我們還可以通過函數返回值來來傳遞動態內存,代碼如下:
char* func()
{
char *p=malloc(100);
return p;
}
void main()
{
char* str=NULL;
str=func();// 給str分配內存
...
free(str);//用完釋放內存
}
malloc函數返回的指針指向“堆區”,但不能通過函數返回值方式返回指向“棧內存”的指針,編譯器將給出“返回局部變量或臨時變量的地址”的警告提示。
char* func(int loginNo)
{
char str1[]="hello tht1";//棧
char str2[]="hello tht2";//棧
if(1==loginNo)
return str1;//警告:返回局部變量或臨時變量的地址
else return str2;//警告:返回局部變量或臨時變量的地址
}
void main()
{
char* str=NULL;
str=func(1);
...
}