邏輯上的分區(qū)
- 棧區(qū)
- 堆區(qū)
- 靜態(tài)區(qū)
- 常量區(qū)
- 代碼區(qū)
代碼區(qū),常量區(qū),靜態(tài)區(qū),堆區(qū),棧區(qū)這個(gè)排列順序按照地址由小到大排列的
代碼區(qū):
只讀的
程序員無需對(duì)其操作,里邊放著代碼編譯之后形成的二進(jìn)制
常量區(qū):
只讀的
例如"acfun",'Q',5,等等的常量,放在常量區(qū)
char *p ="bilibili";
任何試圖修改常量區(qū)內(nèi)容的操作,都會(huì)造成內(nèi)存崩潰
char arr[] ="bilibili";
arr[0] = 'I';
在使用指針操作字符串的時(shí)候一定注意內(nèi)存分區(qū)的問題
strcpy(p, "123");
p[0] = 'I';
printf("%p\n",p);
靜態(tài)區(qū):
使用static關(guān)鍵字修飾的變量放在靜態(tài)區(qū)讨盒,放在靜態(tài)區(qū)的變量只被初始化一次
static int a = 10;
- 如果不給初始值,默認(rèn)相當(dāng)于賦值0
static int b;//默認(rèn)相當(dāng)于給b = 0;
- 使用static修飾的靜態(tài)變量生命周期等于整個(gè)程序的生命周期,即程序退出后才被釋放(iOS編程寫單例類的時(shí)候,要使用static)
int all = 10;//全局變量,實(shí)際上也放在靜態(tài)區(qū)
printf("%p\n",&a);//代碼區(qū),常量區(qū),靜態(tài)區(qū),棧區(qū),的所占空間并不大
printf("%p\n",&all);//里邊局部變量0x7fff5fbff7b0
printf("%p\n",&all);//外邊全局變量0x100001020
(Stack)棧區(qū):
一塊連續(xù)的內(nèi)存,由系統(tǒng)自動(dòng)分配和釋放,不需要程序員手動(dòng)管理,大小也就1M-2M
他是有自己管理的規(guī)則的:棧區(qū)內(nèi)存的分配按照先進(jìn)后出的原則
int a = 10;//先初始化的a
int b = 20;//后初始化的b
printf("%p\n",&a);//地址高
printf("%p\n",&b);//地址低
之前所學(xué)的代碼其實(shí)都放在棧區(qū),只不過沒有出現(xiàn)棧區(qū)溢出的問題
堆區(qū):
內(nèi)存比較大的空間都被堆區(qū)占用著,堆區(qū)是不連續(xù)的內(nèi)存空間,需要咱們程序員手動(dòng)的分配和釋放內(nèi)存
定義一個(gè)指針
int *p =malloc(4);
malloc就是程序員手動(dòng)分配內(nèi)存空間的函數(shù),函數(shù)的參數(shù)放著我們需要的內(nèi)存空間這塊空間就是堆內(nèi)存的一塊空間
printf("%p\n",p);//堆內(nèi)存地址
printf("%d\n",*p);//堆內(nèi)存里邊的值
釋放空間
free(p);
p = NULL;//讓p指向地址0x0.
課上練習(xí)
輸入三個(gè)單詞保存在堆內(nèi)存并輸出
char *word[3] = {0};//指針數(shù)組
char temp1[100] = {0};//用來保存每一個(gè)單詞
printf("請(qǐng)輸入三個(gè)單詞:\n");
for(int i = 0; i < 3; i++) {
scanf("%s",temp1);
word[i] =malloc(sizeof(temp1));//指向堆內(nèi)存地址
strcpy(word[i], temp1);
}
printf("**********************\n");
for(int i = 0; i < 3; i++) {
printf("%s\n",word[i]);
free(word[i]);//若多釋放一塊未開辟的空間,程序會(huì)崩潰掉,這種情況稱為過度釋放
word[i] =NULL;
}
內(nèi)存分配函數(shù)
malloc和free…這些函數(shù)維護(hù)一個(gè)可用內(nèi)存池斩松,并向該程序返回一個(gè)指向這塊內(nèi)存的指針瞻凤。這塊內(nèi)存此時(shí)此刻并沒有以任何方式進(jìn)行初始化憨攒。如果這個(gè)內(nèi)存很重要你可以手動(dòng)進(jìn)行初始化,要么使用calloc函數(shù)阀参。
malloc函數(shù)
函數(shù)原型
void *malloc(size_t size);
- size就是需要分配的內(nèi)存字節(jié)(字符)數(shù)肝集。如果內(nèi)存池中的內(nèi)存可以滿足這個(gè)要求,malloc就返回一個(gè)指向被分配內(nèi)存的起始位置的指針蛛壳。
- malloc所分配的是一塊連續(xù)的內(nèi)存杏瞻。
- 如果內(nèi)存池為空所刀,malloc函數(shù)會(huì)向操作系統(tǒng)請(qǐng)求。如果操作系統(tǒng)無法向malloc提供更多的內(nèi)存捞挥,malloc就會(huì)返回一個(gè)NULL指針浮创。因此要對(duì)malloc返回的指針進(jìn)行檢查,確保其非NULL
- malloc返回一個(gè)void *的指針砌函。標(biāo)準(zhǔn)的void *指針可以轉(zhuǎn)換為其他任何類型的指針斩披。但是,在某些(老式)編譯器可能需要你在轉(zhuǎn)換時(shí)使用強(qiáng)制類型轉(zhuǎn)換
- 對(duì)于要求邊界對(duì)齊的機(jī)器讹俊,malloc所返回的內(nèi)存的起始位置將始終能夠滿足對(duì)邊界對(duì)齊要求最嚴(yán)格的類型的要求垦沉。
char *p1 =malloc(4);
for(int i = 0; i < 4; i++) {
*(p1+i) = 65 + i;
printf("%c\n",*(p1+i));
}
free(p1);
p1 =NULL;
free函數(shù)
函數(shù)原型
void free(void *pointer);
- free的參數(shù)要么是NULL,要么是先前從malloc仍劈、calloc或realloc返回的值厕倍。
- 向free傳遞一個(gè)NULL參數(shù)不會(huì)產(chǎn)生任何效果
calloc函數(shù)
函數(shù)原型
void *calloc(size_t num_elements,size_t element_size);
- calloc也用于分配內(nèi)存。malloc和calloc之間的主要區(qū)別是后者返回指向內(nèi)存的指針之前把它初始化為0贩疙。但顯得效率很低讹弯。
- malloc和calloc它倆請(qǐng)求內(nèi)存數(shù)量的方式不同。calloc的參數(shù)包括所需元素的數(shù)量和每個(gè)元素的字節(jié)數(shù)屋群。根據(jù)這些值,可以計(jì)算出總共需要分配的內(nèi)存坏挠。
char *p2 =calloc(3, 1);第一個(gè)參數(shù)是分配多少個(gè),第二個(gè)參數(shù)是每個(gè)有多少個(gè)字節(jié)
for(int i = 0; i < 3; i++) {
*(p2+i) = 'a';
printf("%c\n",*(p2+i));
}
free(p2);
p2 =NULL;
練習(xí)
使用calloc函數(shù)分配十塊四個(gè)字節(jié)的空間,存放十個(gè)隨機(jī)數(shù)取值范圍在10 - 30之間,然后打印
char *p3 =calloc(10, 4);
for(inti = 0; i < 10; i++) {
*(p3+i) =arc4random()%21 + 10;
printf("%d\n",*(p3+i));
}
free(p3);
realloc函數(shù)
函數(shù)原型
void realloc(void *ptr,size_t new_size);
- realloc函數(shù)用于修改一個(gè)原先已經(jīng)分配的內(nèi)存塊的大小芍躏。這個(gè)函數(shù)可以使一塊內(nèi)存擴(kuò)大或縮小。
- 如果擴(kuò)大降狠,那么這塊內(nèi)存原先的內(nèi)容依然保留对竣,新增加的內(nèi)存添加到原先內(nèi)存塊的后面,新內(nèi)存并未以任何方法進(jìn)行初始化榜配。
- 如果縮小否纬,該內(nèi)存塊尾部的部分內(nèi)存便被拿掉,剩余部分內(nèi)存的原先內(nèi)容依然保留蛋褥。
- 如果原先的內(nèi)存塊無法改變大小临燃,realloc將分配另一塊正確大小的內(nèi)存,并把原先那塊內(nèi)存的內(nèi)容復(fù)制到新的塊上烙心。因此膜廊,在使用realloc之后,你就不能再使用指向舊內(nèi)存的指針淫茵,而應(yīng)該改用realloc所返回的新指針爪瓜。
- 如果realloc函數(shù)的第一個(gè)參數(shù)是NULL,那么它的行為就和malloc一模一樣。
int *p4 =malloc(4);
int *p5 =realloc(p4, 8);給p4重新分配了8個(gè)字節(jié)的空間
*p5 = 10;
*(p5+1) = 20;
*p4 = 11;
*(p4+1) = 21;
printf("%d\n%d\n",*p5,*(p5+1));
printf("%d\n%d\n",*p4,*(p4+1));
memset內(nèi)存設(shè)置函數(shù)
int *p6 =malloc(4);
注意這里聲明的是整型
memset(p6, 65, 16);第一個(gè)參數(shù)放要操作的指針,第二個(gè)參數(shù)放內(nèi)容(整型或者字符型),第三個(gè)參數(shù)放字節(jié)數(shù)
for(inti = 0; i < 4; i++) {
printf("%c\n",*(p6+i));
}
memcpy內(nèi)存內(nèi)容拷貝的函數(shù)
char*p7 =malloc(4);
memcpy(p7, p6, 5);
for(inti = 0; i < 4; i++) {
// *(p7 + i) = 66;
printf("%c\n",*(p7+i));
}
內(nèi)存的比較函數(shù)
跟字符串比較函數(shù)一樣,比較里邊的內(nèi)容,找到第一個(gè)不相同的字符,按照ascii碼表值進(jìn)行作差
int count =memcmp(p7, p6, 5);
printf("%d\n",count);
動(dòng)態(tài)內(nèi)存分配
int *pi;
pi = malloc(100);
if(pi == NULL){
printf("Out of memory!\n");
exit(1);
}
如果分配內(nèi)存成功匙瘪,我們就擁有了一個(gè)指向100個(gè)字節(jié)的指針铆铆。在整型為4個(gè)字節(jié)的機(jī)器上蝶缀,這塊內(nèi)存將被當(dāng)作25個(gè)整型數(shù)組元素的數(shù)組,因?yàn)閜i是一個(gè)指向整型的指針薄货。
技巧
pi = malloc(25*sizeof(int));
常見動(dòng)態(tài)分配錯(cuò)誤
定義一個(gè)不易發(fā)生錯(cuò)誤的內(nèi)存分配器
#define malloc //防止由于其他代碼塊直接塞入程序而導(dǎo)致偶爾直接調(diào)用maloc的行為翁都。增加這個(gè)指令后,如果程序偶爾調(diào)用了malloc菲驴,程序?qū)⒂捎谡Z法錯(cuò)誤無編譯荐吵。
#define MALLOC(num,type) (type*)alloc((num)*sizeof(type))
extern void *alloc(size_t size);
- 忘記檢查內(nèi)存是否分配成功
- 被訪問的內(nèi)存可能保存了其他變量的值
- 傳遞給free的指針必須是從malloc、calloc或realloc函數(shù)返回的指針赊瞬。