動態內存分配_C語言

邏輯上的分區

  • 棧區
  • 堆區
  • 靜態區
  • 常量區
  • 代碼區

代碼區,常量區,靜態區,堆區,棧區這個排列順序按照地址由小到大排列的

代碼區:

只讀的

程序員無需對其操作,里邊放著代碼編譯之后形成的二進制

常量區:

只讀的

例如"acfun",'Q',5,等等的常量,放在常量區

char *p ="bilibili";

任何試圖修改常量區內容的操作,都會造成內存崩潰

char arr[] ="bilibili";
arr[0] = 'I';

在使用指針操作字符串的時候一定注意內存分區的問題

strcpy(p, "123");
p[0] = 'I';
printf("%p\n",p);

靜態區:

使用static關鍵字修飾的變量放在靜態區,放在靜態區的變量只被初始化一次

static int a = 10;
  • 如果不給初始值,默認相當于賦值0
static int b;//默認相當于給b = 0;
  • 使用static修飾的靜態變量生命周期等于整個程序的生命周期,即程序退出后才被釋放(iOS編程寫單例類的時候,要使用static)
int all = 10;//全局變量,實際上也放在靜態區

printf("%p\n",&a);//代碼區,常量區,靜態區,棧區,的所占空間并不大

printf("%p\n",&all);//里邊局部變量0x7fff5fbff7b0

printf("%p\n",&all);//外邊全局變量0x100001020

(Stack)棧區:

一塊連續的內存,由系統自動分配和釋放,不需要程序員手動管理,大小也就1M-2M

他是有自己管理的規則的:棧區內存的分配按照先進后出的原則

int a = 10;//先初始化的a
int b = 20;//后初始化的b

printf("%p\n",&a);//地址高
printf("%p\n",&b);//地址低

之前所學的代碼其實都放在棧區,只不過沒有出現棧區溢出的問題

堆區:

內存比較大的空間都被堆區占用著,堆區是不連續的內存空間,需要咱們程序員手動的分配和釋放內存

定義一個指針

int *p =malloc(4);

malloc就是程序員手動分配內存空間的函數,函數的參數放著我們需要的內存空間這塊空間就是堆內存的一塊空間

printf("%p\n",p);//堆內存地址
printf("%d\n",*p);//堆內存里邊的值

釋放空間

free(p);
p = NULL;//讓p指向地址0x0.

課上練習

輸入三個單詞保存在堆內存并輸出

   char *word[3] = {0};//指針數組
   char temp1[100] = {0};//用來保存每一個單詞
   printf("請輸入三個單詞:\n");
   for(int i = 0; i < 3; i++) {
       scanf("%s",temp1);
       
        word[i] =malloc(sizeof(temp1));//指向堆內存地址
       
       strcpy(word[i], temp1);
    }
   printf("**********************\n");
   for(int i = 0; i < 3; i++) {
       printf("%s\n",word[i]);
       free(word[i]);//若多釋放一塊未開辟的空間,程序會崩潰掉,這種情況稱為過度釋放
        word[i] =NULL;
    }

內存分配函數

malloc和free…這些函數維護一個可用內存池,并向該程序返回一個指向這塊內存的指針。這塊內存此時此刻并沒有以任何方式進行初始化。如果這個內存很重要你可以手動進行初始化,要么使用calloc函數。

malloc函數

函數原型

void *malloc(size_t size);
  • size就是需要分配的內存字節(字符)數。如果內存池中的內存可以滿足這個要求,malloc就返回一個指向被分配內存的起始位置的指針。
  • malloc所分配的是一塊連續的內存。
  • 如果內存池為空,malloc函數會向操作系統請求。如果操作系統無法向malloc提供更多的內存,malloc就會返回一個NULL指針。因此要對malloc返回的指針進行檢查,確保其非NULL
  • malloc返回一個void *的指針。標準的void *指針可以轉換為其他任何類型的指針。但是,在某些(老式)編譯器可能需要你在轉換時使用強制類型轉換
  • 對于要求邊界對齊的機器,malloc所返回的內存的起始位置將始終能夠滿足對邊界對齊要求最嚴格的類型的要求。
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函數

函數原型

void free(void *pointer);
  • free的參數要么是NULL,要么是先前從malloc、calloc或realloc返回的值。
  • 向free傳遞一個NULL參數不會產生任何效果

calloc函數

函數原型

void *calloc(size_t num_elements,size_t element_size);
  • calloc也用于分配內存。malloc和calloc之間的主要區別是后者返回指向內存的指針之前把它初始化為0。但顯得效率很低。
  • malloc和calloc它倆請求內存數量的方式不同。calloc的參數包括所需元素的數量和每個元素的字節數。根據這些值,可以計算出總共需要分配的內存。
char *p2 =calloc(3, 1);第一個參數是分配多少個,第二個參數是每個有多少個字節

for(int i = 0; i < 3; i++) {
    *(p2+i) = 'a';
    printf("%c\n",*(p2+i));
}

free(p2);

p2 =NULL;

練習

使用calloc函數分配十塊四個字節的空間,存放十個隨機數取值范圍在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函數

函數原型

void realloc(void *ptr,size_t new_size);
  • realloc函數用于修改一個原先已經分配的內存塊的大小。這個函數可以使一塊內存擴大或縮小。
  • 如果擴大,那么這塊內存原先的內容依然保留,新增加的內存添加到原先內存塊的后面,新內存并未以任何方法進行初始化。
  • 如果縮小,該內存塊尾部的部分內存便被拿掉,剩余部分內存的原先內容依然保留。
  • 如果原先的內存塊無法改變大小,realloc將分配另一塊正確大小的內存,并把原先那塊內存的內容復制到新的塊上。因此,在使用realloc之后,你就不能再使用指向舊內存的指針,而應該改用realloc所返回的新指針。
  • 如果realloc函數的第一個參數是NULL,那么它的行為就和malloc一模一樣。
int *p4 =malloc(4);
int *p5 =realloc(p4, 8);給p4重新分配了8個字節的空間
  
*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內存設置函數

int *p6 =malloc(4);

注意這里聲明的是整型

memset(p6, 65, 16);第一個參數放要操作的指針,第二個參數放內容(整型或者字符型),第三個參數放字節數

for(inti = 0; i < 4; i++) {
printf("%c\n",*(p6+i));

}

memcpy內存內容拷貝的函數

char*p7 =malloc(4);

memcpy(p7, p6, 5);
 
for(inti = 0; i < 4; i++) {
//        *(p7 + i) = 66;
printf("%c\n",*(p7+i));
}

內存的比較函數

跟字符串比較函數一樣,比較里邊的內容,找到第一個不相同的字符,按照ascii碼表值進行作差

int count =memcmp(p7, p6, 5);
printf("%d\n",count);

動態內存分配

int *pi;
pi = malloc(100);
if(pi == NULL){
    printf("Out of memory!\n");
    exit(1);
}

如果分配內存成功,我們就擁有了一個指向100個字節的指針。在整型為4個字節的機器上,這塊內存將被當作25個整型數組元素的數組,因為pi是一個指向整型的指針。

技巧

pi = malloc(25*sizeof(int));

常見動態分配錯誤

定義一個不易發生錯誤的內存分配器

#define malloc //防止由于其他代碼塊直接塞入程序而導致偶爾直接調用maloc的行為。增加這個指令后,如果程序偶爾調用了malloc,程序將由于語法錯誤無編譯。
#define MALLOC(num,type) (type*)alloc((num)*sizeof(type))
extern void *alloc(size_t size);
  • 忘記檢查內存是否分配成功
  • 被訪問的內存可能保存了其他變量的值
  • 傳遞給free的指針必須是從malloc、calloc或realloc函數返回的指針。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容