問:
棧區(qū)堆區(qū)靜態(tài)區(qū)的大小是固定的嗎?如果棧區(qū)滿了,會向后2者借位置嗎?
ChatAI答:
棧區(qū)、堆區(qū)和靜態(tài)區(qū)的大小通常是由操作系統(tǒng)或編譯器預定義的,不是固定的。這些區(qū)域的大小通常受到多種因素的影響,如系統(tǒng)物理內存大小、進程虛擬地址空間的大小、編譯器和操作系統(tǒng)的實現等等。
當棧區(qū)滿了時,是不能向堆區(qū)或靜態(tài)區(qū)借位置的。棧區(qū)和堆區(qū)是兩個獨立的內存區(qū)域,它們使用不同的指針來管理內存空間。當棧區(qū)滿了,意味著當前棧幀中的內存空間已經全部使用完畢,如果需要分配更多的內存,需要在棧上創(chuàng)建一個新的棧幀來繼續(xù)分配內存。但是,由于棧區(qū)的大小通常是有限的,當創(chuàng)建的棧幀數量過多或棧幀過大時,就會出現棧溢出的情況,導致程序崩潰。
另一方面,堆區(qū)和靜態(tài)區(qū)可以被程序共享使用,甚至可以跨越多個棧幀和函數調用。堆區(qū)由malloc、realloc、calloc等函數動態(tài)分配,當內存不夠時,它可以通過請求更多的虛擬內存來擴展堆區(qū)的大小。靜態(tài)區(qū)的大小在編譯時就已經確定,通常包括全局變量、靜態(tài)變量、常量等,而且靜態(tài)區(qū)的大小不會隨著程序運行而改變。
總的來說,棧區(qū)、堆區(qū)和靜態(tài)區(qū)的大小通常是由各種因素共同決定的,并不是固定的。程序在運行時會預先將這些區(qū)域劃分好,而當其中的某個區(qū)域已經滿了時,就需要通過不同的方式來處理。
動態(tài)函數的介紹
malloc和free
C語言提供了一個動態(tài)內存開辟的函數:
void* malloc (size_t size);
這個函數向內存申請一塊連續(xù)可用的空間,并返回指向這塊空間的指針。
。如果開辟成功,則返回一個指向開辟好空間的指針。
。如果開辟失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查。
。返回值的類型是void*,所以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自己來決定。
。如果參數size為0,malloc的行為是標準是未定義的,取決于編譯器。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//向內存申請10個整型的空間
int* p = (int*)malloc(10 * sizeof(int));
//int* p = (int*)malloc(INT_MAX);
if (p == NULL)
{
//打印錯誤原因的一個方式
printf("%s\n", strerror(errno));
}
else
{
//正常使用空間
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
C語言提供了一個另外的函數free,專門是用來做動態(tài)內存的釋放和回收的,函數原型如下:
void free(void* ptr);
free函數用來釋放動態(tài)開辟的內存。
。如果參數ptr指向的空間不是動態(tài)開辟的,那free函數的行為是未定位的。
。如果參數ptr是NULL指針,則函數什么事都不做。
calloc
C語言還提供了一個函數叫calloc,calloc函數也用來動態(tài)內存分配。原型如下:
void* calloc(size_t num,size_t size);
。函數的功能為num個大小為size的元素開辟一塊空間,并且把空間的每個字節(jié)初始化為0。
。與函數malloc的區(qū)別只在于calloc會在返回地址之前把申請的空間的每個字節(jié)初始化為全0.
問:
有calloc函數了還要malloc函數做什么?
ChatAI答:
calloc函數與malloc函數都是用來動態(tài)分配內存的函數,二者的不同之處在于內存分配和初始化的方式不同。
- malloc函數分配內存空間時,內存中的數據不會被初始化,需要手動初始化才能使用。如果不進行初始化操作,申請的內存空間中可能殘留著之前的垃圾數據,這就會對程序產生潛在的安全隱患。
- 而calloc函數分配內存空間時,不僅會申請指定大小的內存,還會將其初始化為0。
因此,如果程序需要申請一塊內存,并且希望該內存全部清零,那么就應該使用calloc函數;如果程序需要申請一塊內存,但不需要初始化其中的數據,則需要使用malloc函數。
注意,calloc函數與malloc函數的返回值是相同的,它們都返回指向所分配內存的首字節(jié)的指針。需要注意的是,在使用calloc函數分配內存時,分配的空間大小第一個參數n和第二個參數size是傳入的字節(jié)數的形式,例如calloc(n, size)中所請求的內存大小為 n*size 個字節(jié)。
總之,malloc函數和calloc函數都有各自的使用場景,根據實際情況選擇使用。在申請內存時,應該根據需要進行初始化或者清零操作,確保程序的正確性和安全性。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
int * p = (int*)calloc(10 , sizeof(int));
if (p == NULL)
{
//打印錯誤原因的一個方式
printf("%s\n", strerror(errno));
}
else
{
//正常使用空間
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
問:
如果malloc后又realloc將動態(tài)內存空間設置小了,再free,那么之前被改小的部分是在realloc的時候就釋放了,還是要直到free的時候才釋放?
ChatAI答:
如果使用realloc函數將先前申請的動態(tài)內存空間設置為比原先更小的大小,那么被裁剪的那一部分內存空間會立即被釋放,即在realloc函數調用之后就被釋放了。程序無法再訪問被裁剪的那一部分內存空間,但是其內存空間并沒有馬上被OS收回,而是被保留在堆上,等待以后可能的內存分配請求。
當使用free函數釋放指針所指的內存塊時,其行為就與動態(tài)分配內存的方式是無關的。而是由malloc,calloc,realloc之類的函數所使用的堆管理器,根據內部的算法來決定如何回收內存塊。因此,即使調用realloc函數將先前申請的內存縮小,被裁剪的那一部分內存空間也可能不會立即被OS收回,而可能暫時被保留在堆上,等待可能的內存分配請求。
在實際使用中,為了避免內存泄漏,建議在不需要使用先前申請的動態(tài)內存時,立即使用free函數將其釋放,而不是讓其保留在堆上。因此,如果在realloc函數之后又立即使用free函數釋放指針所指的內存塊,則被裁剪的那一部分內存空間應該能被立即回收,而不必等到程序結束或操作系統(tǒng)重啟等時機。
realloc
realloc函數的出現讓動態(tài)內存管理更加靈活。
為了合理的內存,我們會一定程度對內存的大小做靈活的調整。realloc函數可以做到對動態(tài)開辟內存大小的調整。函數原型如下:
void* realloc(void* ptr,size_t size);
。ptr是要調整的內存地址
。size調整之后新大小
。返回值為調整之后的內存起始位置
。這個函數調整原內存空間大小的基礎上,還會將原來內存中的數據移動到新的空間
。realloc在調整內存空間的是存在兩種情況:
。情況1:原有空間之后有足夠大的空間
問:realloc函數調整后返回的指針地址和先前malloc開辟返回的地址會不會發(fā)生改變?
ChatAI答:
realloc函數的作用是調整先前通過malloc或calloc函數動態(tài)分配的內存塊的大小,并返回指向調整后內存塊的新地址的指針。當內存塊當前的大小不足以滿足分配新內存時,realloc函數則會在其他地方重新申請新的內存塊,并將先前內存塊的數據復制到新的地址中,最后釋放原先的內存塊。這樣做可以保證分配的新內存塊的大小不會小于要求的大小,并且可以盡量減少內存的碎片。
如果調用realloc函數會導致內存大小實際上并沒有發(fā)生變化,那么realloc函數就可能返回與先前內存塊相同的地址。但是,調用realloc函數重新調整內存塊大小時,返回的指針地址可能會發(fā)生改變。具體來說,如果realloc函數需要在其他地方重新申請內存塊,則返回的指針地址將是新內存塊的首地址;如果內存塊可以直接調整大小,則返回的指針地址可能與先前的地址相同,但是不能保證這種情況始終發(fā)生。文章來源:http://www.zghlxwxcb.cn/news/detail-459402.html
因此,為了保險起見,在使用realloc函數時應該始終將其返回的指針賦值給一個新的指針變量,并且在使用完畢后,應該使用新指針變量來訪問內存塊,而不是依賴先前的指針變量。這樣可以避免內存訪問錯誤和其它潛在的問題,并提高程序的可靠性和穩(wěn)定性。文章來源地址http://www.zghlxwxcb.cn/news/detail-459402.html
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
int * p = (int*)malloc(20);
if (p == NULL)
{
//打印錯誤原因的一個方式
printf("%s\n", strerror(errno));
}
else
{
//正常使用空間
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = 3 * i;
}
for (i = 0; i < 5; i++)
{
printf("%d ", *(p + i));
}
}
printf("\n");
//realloc使用的注意事項:
//1.如果p指向的空間之后有足夠的內存空間可以追加,則直接追加,后返回p
//2.如果p指向的空間之后沒有足夠的內存空間可以追加,則realloc函數會重新找一個新的內存區(qū)域
//開辟一塊滿足需求的空間,并且把原來內存中的數據拷貝回來,釋放舊的內存空間,最后返回新開辟
//的內存空間的地址
//3.得用一個新的變量來接收realloc函數的返回值(一般情況沒什么,但若realloc申請失敗返回了NULL,
//就會把原指針給改成NULL,這樣原來的內存也找不到了。)
int* p2 = (int*)realloc(p, 40);
printf("%p %p\n", p, p2);
//free(p); ?。?!這是錯誤的多此一舉
//p = NULL; realloc函數已經自動將p處理了
int i = 0;
for (i = 5; i < 10; i++)
{
*(p2 + i) = 3 * i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p2);
p2 = NULL;
return 0;
}
到了這里,關于第五十三天學習記錄:C語言進階:動態(tài)內存管理Ⅰ的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!