一、動態內存經典筆試題分析
分析錯誤并改正
題目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;
}
錯誤的原因:
1.str傳給p的時候(值傳遞),p是str的臨時拷貝,有自己獨立的空間,當GetMemory函數內部申請了空間后,地址放在p中時,str依然是NULL。當GetMemory函數返回之后,strcpy拷貝的時候,形成了非法訪問內存
2.在GetMemory函數內部,動態申請了內存,但是沒有釋放,會內存泄露
3.添加對 malloc 返回值的檢查,避免分配失敗時操作空指針。
改正(采用傳址調用):
void GetMemory(char** p)
{*p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(&str);// 傳遞指針的地址if(str != NULL){strcpy(str, "hello world");printf(str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}
或者返回動態分配的指針:
char* GetMemory()
{char* p = (char *)malloc(100);return p;//return (char*)malloc(100);
}
void Test(void)
{char *str = NULL;str = GetMemory();if(str != NULL){strcpy(str, "hello world");printf(str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}
題目2
返回棧空間地址的問題
char *GetMemory(void)
{char p[] = "hello world";// 局部數組,存儲在棧內存return p;// 函數結束后,棧內存被釋放,p的地址失效
}
void Test(void)
{char *str = NULL;str = GetMemory();//函數返回后,局部變量 p 的內存被系統回收,但 str 仍指向該地址,形成懸掛指針。printf(str);// 可能輸出亂碼或崩潰
}
int main()
{Test();return 0;
}
錯誤的原因:
GetMemory 函數內部定義了一個局部數組 char p[] = "hello world";,并返回其指針。
問題:局部數組存儲在棧內存中,函數結束后棧內存被釋放,此時返回的指針指向無效內存(懸掛指針)。后續通過 str 訪問會導致未定義行為(如輸出亂碼或程序崩潰)。
懸空指針:是指向已經被釋放或無效內存區域的指針。這種指針雖然保留了之前的內存地址,但地址中的內容可能已被系統回收或重新分配,訪問它會導致 未定義行為(如程序崩潰、數據錯誤、輸出亂碼等)。
改正:
1.返回字符串常量的指針:
char *GetMemory(void)
{char* p = "hello world";return p;// 字符串常量存儲在只讀區,生命周期為整個程序//return "hello world";
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}
2.使用動態內存分配:
char *GetMemory(void)
{char* p = (char*)malloc(strlen("hello world")+1);// +1 用于容納 '\0'if(p != NULL){strcpy(p,"hello world");}return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();if(str != NULL){printf("%s\n",str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}
3.使用靜態變量:
char *GetMemory(void)
{static char p[] = "hello world";// 靜態變量生命周期為整個程序return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}
題目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;
}
錯誤的原因:malloc的返回值沒有判斷并且存在內存泄露(沒有free)
改正:
void GetMemory(char **p, int num)
{*p = (char *)malloc(num);
}
void Test(void)
{char *str = NULL;GetMemory(&str, 100);if(str != NULL){strcpy(str, "hello");printf("%s\n",str);}free(str);str = NULL:
}
int main()
{Test();return 0;
}
題目4
void Test(void)
{char *str = (char *) malloc(100);//未對malloc返回值進行判斷strcpy(str, "hello");free(str);if(str != NULL)// str 未被置空,條件仍為真{strcpy(str, "world");// 操作已釋放的內存(未定義行為)printf(str);// 可能崩潰或輸出亂碼}
}
int main()
{Test();return 0;
}
錯誤的原因:未對malloc返回值進行判斷,str 未被置空,是一個懸空指針
改正:
void Test(void)
{char *str = (char *) malloc(100);if(str != NULL){strcpy(str, "hello");printf("%s\n", str);}free(str);str = NNULL;// 置空指針,避免誤用// 后續操作需確保指針有效if(str != NULL){// 此處代碼永遠不會執行strcpy(str, "world");printf(str);}
}
int main()
{Test();return 0;
}
二、柔性數組
C99中,結構中的最后?個元素允許是未知大小的數組,這就叫做『柔性數組』成員
struct st_type
{int i;int arr[0];//柔性數組成員
};
有些編譯器會報錯?法編譯可以改成:
struct st_type
{int i;int arr[];//柔性數組成員
};
2.1?柔性數組的特點
1.結構中的柔性數組成員前?必須至少?個其他成員。
2.sizeof返回的這種結構大小不包括柔性數組的內存。
3.包含柔性數組成員的結構用malloc()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
struct st_type
{//1.結構中的柔性數組成員前?必須?少?個其他成員。int i;int arr[];//柔性數組成員
}type_a;
int main()
{//2.sizeof返回的這種結構??不包括柔性數組的內存。printf("%d\n", sizeof(type_a));//輸出的是4return 0;
}
//3.包含柔性數組成員的結構?malloc()函數進?內存的動態分配,并且分配的內存應該?于結構的??,以適應柔性數組的預期??。
struct S
{int i;char arr[];
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S) + 10*sizeof(char));if(ps == NULL){return 1;}ps->i = 100;printf("%d\n", ps->i);int i = 0;for(i = 0;i < 10; i++){ps->arr[i] = 'Q';}for(i = 0;i < 10; i++){printf("%c ",ps->arr[i]);//Q Q Q Q Q Q Q Q Q Q}printf("\n");//增容struct S* ptr = (struct S*)realloc(ps,sizeof(struct S) + 20*sizeof(char));if(ptr != NULL){ps = ptr;}else{perror("realloc");return 1;}for(i = 10;i < 20; i++){ps->arr[i] = 'A';}for(i = 0;i < 20; i++){printf("%c ",ps->arr[i]);//Q Q Q Q Q Q Q Q Q Q A A A A A A A A A A}free(ps);ps = NULL;return 0;
}
柔性數組成員?malloc()函數進?內存的動態分配后獲得空間,對結構體進行計算大小,還是不會算進去,只算除了柔性數組外的大小
printf("%d\n", sizeof(struct S));//4
2.2?柔性數組的優勢
1.方便內存釋放
2.這樣有利于訪問速度
代碼一:
struct S
{int i;char arr[];
};
int main()
{int i = 0;struct S* p = (struct S*)malloc(sizeof(struct S) + 100 * sizeof(char));//業務處理 p->i = 100;for (i = 0; i < 100; i++){p->arr[i] = i;}free(p);p = NULL;return 0;
}
1.malloc一次 ? ? ?2.free一次 ? ? ?3.空間連續
代碼二:
struct S
{int i;char* arr;
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S));if(ps == NULL){perror("malloc");return 1;} ps->i = 100;ps->arr = (char*)malloc(sizeof(char) * 10);if(ps->arr == NULL){perror("malloc->arr");return 1;}//使用int i = 0;for(i = 0;i < 10; i++){ps->arr[i] = 'Q';}//釋放free(ps->arr);ps->arr = NULL;free(ps);ps = NULL;return 0;
}
1.malloc兩次 ? ? ?2.free兩次 ? ? ?3.內存空間不連續(內存碎片多,浪費空間)
三、內存區域劃分
C/C++程序內存分配的幾個區域:
1. 棧區(stack):在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時 這些存儲單元?動被釋放。棧內存分配運算內置于處理器的指令集中,效率很?,但是分配的內 存容量有限。棧區主要存放運?函數?分配的局部變量、函數參數、返回數據、返回地址等。
2. 堆區(heap):?般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。分配? 式類似于鏈表。
3. 數據段(靜態區):(static)存放全局變量、靜態數據。程序結束后由系統釋放。
4. 代碼段:存放函數體(類成員函數和全局函數)的?進制代碼。