教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

c/c++培訓(xùn)C語言核心知識總結(jié)(八)

更新時間:2016年10月21日16時39分 來源:傳智播客C++培訓(xùn)學(xué)院 瀏覽次數(shù):

八、堆區(qū)內(nèi)存
 
#include <stdlib.h>
 
1. void* malloc(n * sizeof(int));
請求 n 個連續(xù)的、每個長度是一個int大小的堆空間,如果創(chuàng)建成功,將返回這個堆空間的首地址,如果創(chuàng)建失敗,返回 NULL;
 
2. void* calloc(n, sizoef(int));
請求 n 個連續(xù)的、每個長度是一個int大小的堆空間,如果創(chuàng)建成功,將返回這個堆空間的首地址,如果創(chuàng)建失敗,返回NULL ;
(和 malloc() 函數(shù)的區(qū)別在于,calloc()在創(chuàng)建成功后,會把空間自動初始化為 0 );
 
3. void *realloc(p, n * sizeof(int));
給一個已經(jīng)分配了地址的指針 p 重新分配空間,p 是原來空間的首地址,n * sizeof(int) 基于這個首地址重新分配的大小;
1) 如果當(dāng)前內(nèi)存段后面有足夠的內(nèi)存空間,那么就直接擴(kuò)展這段內(nèi)存,realloc()返回原來的首地址;
2) 如果當(dāng)前內(nèi)存段后面沒有足夠的內(nèi)存空間,那么系統(tǒng)會重新向內(nèi)存樹申請一段合適的空間,并將原來空間里的數(shù)據(jù)塊釋放掉,
而且 realloc() 會返回重新申請的堆空間的首地址;
3) 如果創(chuàng)建失敗,返回 NULL, 此時原來的指針依然有效;
 
4.  void free();
1) free(p); 只是釋放了申請的內(nèi)存,系統(tǒng)將這塊內(nèi)存標(biāo)記為可用。也就是可以被其他進(jìn)程使用,但是并不改變 p 的指向;
2) p 所指向的內(nèi)存空間被釋放,所以其他程序就有機(jī)會使用這段空間了,相當(dāng)于 p 指向了不屬于自己的空間,里面的數(shù)據(jù)也是未知的。
(這個就叫野指針)
3) 為了避免野指針,最好在 free(p)之后,將 p = NULL; void *(0);
4) free()函數(shù)在執(zhí)行的時候,其實(shí)是把這個塊內(nèi)存返回了內(nèi)存紅黑樹上,讓別人可以使用這塊內(nèi)存。
從邏輯上來說,釋放p之后,你是不能再訪問原先p指向的這塊內(nèi)存了,但是現(xiàn)在操作系統(tǒng)沒有做到,
所以你還是可以訪問到這塊內(nèi)存的,只是里面可能存有的數(shù)據(jù)不屬于你。
free(p)之后,其實(shí)系統(tǒng)并沒有做數(shù)據(jù)清空處理,所以你既可以訪問這個空間,也可以用里面的值。
但是嚴(yán)格意義上來說,這樣做是非法的!會造成野指針!
 
 
示例:
// 如何釋放自定義函數(shù)內(nèi)申請的堆空間
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char *funcA();
char *funcB();
 
int a = 10; // 全局 初始化區(qū)域
char *p1; // 全局 為初始化區(qū)域
 
int main(void)
{
int b; // 棧區(qū)
char arr[] = "hello"; // 棧區(qū)
char *p2; // p2 在棧區(qū)
const char *p3 = "world!"; // p3 在棧區(qū),"world!\0" 在常量區(qū)
 
static int c = 0; // 靜態(tài)區(qū) 初始化區(qū)域
 
char *p;
 
p1 = (char *)malloc(20); // p1 指向堆區(qū) 20個字節(jié)
 
memset(p1, 0, sizeof(char) * 20); // 使用memset()函數(shù)將內(nèi)存空間初始化為 0
strcpy(p1, "Are you Sleep?"); // "Are you Sleep?" 是在常量區(qū)
 
printf("%s\n", p1); // p1 指向的 拷貝到堆空間的 "Are you Sleep?" 的首地址,通過首地址打印這個字符串
 
p2 = funcB(); // p2 接收了funcB()回傳堆空間首地址,可以通過這個地址找到funcA()申請的堆空間
 
free(p2); // 也可通過 p2 釋放 自定義函數(shù)里申請的對空勁啊
free(p1);
 
p1 = NULL; // 安全起見,釋放堆空間指針后, 重置將指針變量置為 NULL
p2 = NULL;
 
return 0; // 返回 0 給系統(tǒng)表示 main()正常執(zhí)行結(jié)束,也就代表程序執(zhí)行結(jié)束
}
 
char *funcA()
{
int a = 10;
const char *pa = "1234567";  // pa 在棧區(qū), "1234567\0" 在常量區(qū)
char *pb = NULL;    // pb 在棧區(qū),pb 占4字節(jié)(32bit system)
 
pb = (char *)malloc(20); // pb 指向了一個20個字節(jié)大小的堆空間
strcpy(pb, "Yes, I'm!"); // 拷貝 字符串給 pb,"Yes, I'm!\0" 在常量區(qū)
 
return pb; // 返回 指針變量 pb 保存的堆空間首地址給調(diào)用函數(shù)funcB()
}
 
char *funcB()
{
char *pa = NULL; // pa 是一個棧上的指針變量
pa = funcA(); // pa 接收了 funcA()函數(shù)返回的堆空間地址
return pa; // 返回指針變量 pa 保存的堆空間首地址給調(diào)用函數(shù) main();
}
 
 
論空間分配速度:
棧區(qū)確實(shí)略快于堆區(qū),
使用棧的時候,是直接從分配的地址里讀取值,放到寄存器里,然后再放到目標(biāo)地址。
使用堆的時候,是先將分配的地址放到寄存器里,然后再從這個地址里取值,再放到寄存器里,再放到目標(biāo)地址。
 
論空間訪問速度:
棧區(qū)和堆區(qū)是一樣的,都是一個直接尋址的過程,沒有區(qū)別。
 
 
CPU -> 寄存器 > L1 > L2 > L3 (屬于緩存) > RAM(內(nèi)存) > SRAM(主板的存儲器) >  硬盤
CPU 只和 寄存器做數(shù)據(jù)存取,寄存器是用來存儲臨時數(shù)據(jù)的,對于需要重復(fù)操作的數(shù)據(jù),會放到緩存里。
不管是寄存器還是緩存,數(shù)據(jù)都來自于內(nèi)存, 內(nèi)存呢又分為四個區(qū)....
本文版權(quán)歸傳智播客C++培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處。謝謝!
作者:傳智播客C/C++培訓(xùn)學(xué)院
首發(fā):http://m.fskzgqt.cn/c/ 
0 分享到:
和我們在線交談!