?個人主頁:?熬夜學編程的小林
💗系列專欄:?【C語言詳解】?【數據結構詳解】
動態內存管理
1、動態內存經典筆試題分析
1.1、題目1
1.2、題目2
1.3、題目3
1.4、題目4
2、柔性數組
2.1、柔性數組的特點
2.2、柔性數組的使用
2.3、柔性數組的優勢
3、總結C/C++中程序內存區域劃分
總結
1、動態內存經典筆試題分析
1.1、題目1
void GetMemory(char *p){p = (char *)malloc(100);}
void Test(void){char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);}int main(){Test();return 0;}
請問運行Test 函數會有什么樣的結果?
根據C語言順序結構原則,進入Test()函數之后逐條執行,先創建一個指針變量str指向NULL,再執行GetMemory函數,將 str 傳入函數,然后動態開辟100字節空間,但是指針變量 p 出了函數會自動銷毀,然后執行strcpy函數,但是此處str 指向NULL, 程序對NULL進行解引用操作,程序崩潰 ,前面函數動態開辟內存沒有手動釋放,因此 會導致內存泄漏 。

1.2、題目2
char *GetMemory(void){char p[] = "hello world";return p;}
void Test(void){char *str = NULL;str = GetMemory();printf(str);}int main(){Test();return 0;}
請問運行Test 函數會有什么樣的結果?
根據C語言順序結構原則,進入Test()函數之后逐條執行,先創建一個指針變量str指向NULL,再執行GetMemory函數,將數組首元素地址作為返回值返回給str,但是此處是在棧區開辟的空間,函數結束后就會銷毀,此時str就為野指針, 打印野指針(非法訪問內存)就會出現肉眼看不懂的文字。

1.3、題目3
void GetMemory(char **p, int num){*p = (char *)malloc(num);}
void Test(void){char *str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);}int main(){Test();return 0;}
請問運行Test 函數會有什么樣的結果?
根據C語言順序結構原則,進入Test()函數之后逐條執行,先創建一個指針變量str指向NULL,再執行GetMemory函數,將str的地址傳入函數,將*p開辟100個內存空間,即給str開辟100個內存空間,然后執行strcpy函數,將hello\0拷貝到str,再將str打印出來,此處沒有太大問題,唯一的問題是 str指向的空間是動態開辟的,沒有手動釋放,會導致內存泄漏。

1.4、題目4
void Test(void){char *str = (char *) malloc(100);strcpy(str, "hello");free(str);if(str != NULL){strcpy(str, "world");printf(str);}}
int main()
{Test();return 0;
}
請問運行Test 函數會有什么樣的結果?
根據C語言順序結構原則,進入Test()函數之后逐條執行,先創建一個指針變量str指向動態開辟100字節的空間,然后將該空間拷貝hello\0,然后釋放該空間(只是不能正常使用,實際還是指向該位置),此時str還是指向動態開辟的空間,因此指向strcpy函數,將world\0拷貝到str,然后打印str,即 打印出world,只是str為野指針,即非法訪問內存。

2、柔性數組
也許你從來沒有聽說過柔性數組(flexible array)這個概念,但是它確實是存在的。
C99 中, 結構中 的 最后?個元素 允許是 未知大小的數組 ,這就叫做『柔性數組』成員。
例如:
typedef struct st_type
{int i;int a[0];//柔性數組成員
}type_a;
有些編譯器會報錯無法編譯可以改成:
typedef struct st_type
{int i;int a[];//柔性數組成員
}type_a;
2.1、柔性數組的特點
? 結構中的柔性數組成員前面必須至少?個其他成員。? sizeof 返回的這種結構大小不包括柔性數組的內存。? 包含柔性數組成員的結構用malloc ()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
例如:
typedef struct st_type
{int i;int a[0];//柔性數組成員
}type_a;
int main()
{printf("%d\n", sizeof(type_a));//輸出的是4return 0;
}
2.2、柔性數組的使用
//代碼1
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{int i;int a[0];//柔性數組成員
}type_a;
int main()
{int i = 0;type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));//業務處理p->i = 100;for(i=0; i<100; i++){p->a[i] = i;}free(p);return 0;
}
這樣柔性數組成員a,相當于獲得了100個整型元素的連續空間。
2.3、柔性數組的優勢
上述的 type_a 結構也可以設計為下面的結構,也能完成同樣的效果
//代碼2
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{int i;int *p_a;
}type_a;
int main()
{type_a *p = (type_a *)malloc(sizeof(type_a));p->i = 100;p->p_a = (int *)malloc(p->i*sizeof(int));//業務處理for(i=0; i<100; i++){p->p_a[i] = i;}//釋放空間free(p->p_a);p->p_a = NULL;free(p);p = NULL;return 0;
}
上述 代碼 1 和 代碼 2 可以完成同樣的功能,但是 方法 1 的實現有兩個好處:
第?個好處是:方便內存釋放
如果我們的代碼是在?個給別人用的函數中,你在里面做了?次內存分配,并把整個結構體返回給用戶。用戶調用free可以釋放結構體,但是用戶并不知道這個結構體內的成員也需要free,所以你不能指望用戶來發現這個事。所以,如果我們把結構體的內存以及其成員要的內存?次性分配好了,并返回給用戶?個結構體指針,用戶做?次free就可以把所有的內存也給釋放掉。
第?個好處是:這樣有利于訪問速度.
連續的內存有益于提高訪問速度,也有益于減少內存碎片。(其實,我個?覺得也沒多高了,反正你跑不了要用做偏移量的加法來尋址)
擴展閱讀:
C語?結構體里的數組和指針
https://coolshell.cn/articles/11377.html

3、總結C/C++中程序內存區域劃分

C/C++程序內存分配的幾個區域:
1. 棧區(stack):在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。 棧區主要存放運行函數而分配的局部變量、函數參數、返回數據、返回地址等。2. 堆區(heap):?般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。分配方式類似于鏈表。3. 數據段(靜態區):(static)存放全局變量、靜態數據。程序結束后由系統釋放。4. 代碼段:存放函數體(類成員函數和全局函數)的?進制代碼。
總結
本篇博客就結束啦,謝謝大家的觀看,如果公主少年們有好的建議可以留言喔,謝謝大家啦!