c语言malloc引用类型作参数,C语言动态内存函数的理解和总结
第一:內存的使用
內存可以分為以下三個主要的部分:棧區、堆區、靜態區
棧區(stack):存放的是局部變量、函數的形參等都是在該區上存放的。
堆區(heap):動態內存函數開辟的空間。比如malloc realloc等函數。
靜態區:存放的是全局變量或者靜態變量(由static關鍵字修飾的變量)。
第二:動態內存函數
2.1 存在的價值
動態內存函數可以向內存申請大小可變的的空間,比如創建長度可以改變的數組時,就需要用到動態內存函數malloc和realloc函數來實現。
但是C語言也是支持創建可變長度數組的(C99是支持的,以前是不支持的。如gcc編譯器是支持的)
在棧上開辟的空間大小是固定的,是不可改變的。
數組在定義時,其大小必須指明大小,不能是變量,因為其所需的內存是在編譯時分配。
根據需求,我們所需要的的空間只有在運行時,才可知,則數組在編譯時開辟空間的方式就沒法滿足,因此需要動態開辟內存空間。
如:
但是VS2019是不支持的。
2.2 動態內存分配函數malloc和free
2.2.1 malloc函數介紹
作 用:向內存的堆區申請一塊連續可用的空間:Allocates memory blocks.
函數原型:void *malloc( size_t size );
參 數:size:Bytes to allocate(申請空間大小,單位:字節)
返 回 值:返回void類型的指針(指向所申請空間的地址)(申請成功)
返回NULL(空指針)(如果沒有足夠內存空間可用時,申請失敗)
注 意:返回類型是void類型的指針,因此使用的時候根據需求對其進行強制轉化為所需要的類型的指針變量。
如果參數值為0,C對此沒有做出明確解釋,具體結果,依據編譯器而定,因此,盡量不要出現這種情況。
需要對malloc的返回值進行檢查,因為有可能申請失敗的情況。
因為返回的是void類型的指針,因此需要對結果進行相應的強制類型轉化。
2.2.2 free函數的介紹
作 用:釋放和回收在堆區動態開辟的內存空間:Deallocates or frees a memory block.
函數原型:void free( void *memblock );
參 數:將要被釋放的內存空間的地址
返 回 值:無返回值
注 意:只能釋動在堆區上開辟的態內存的地址,否則報錯。
只能釋放一次,不能釋放多次。
free函數沒有將指針設為NULL的能力,即指針的值沒有發生變化,因此需要手動將其設為NULL,這樣避免后面不小心進行非法訪問內存的情況發生。
如果沒有free,則會發生內存泄露的情況,即申請的空間不釋放,也不同,別人也沒法用,
如果沒有free,只有當程序結束時,系統自動還給內存,但是如果不結束,且會多次申請且不釋放,則會發生嚴重的內存泄露。
因此要做到,誰申請,誰使用,誰釋放,誰使其設為空的原則。
2.2.3 malloc和free函數的使用
開辟成功時:
#include
#include
#include
#include
int main()
{
int i;
int len = 10;
// 向內存開辟10個連續整型的空間,申請成功時,把空間的地址賦值給arr
int* arr = (int*)malloc(len * sizeof(int));
// 判斷開辟內存空間是否成功,因為會開辟失敗
if (NULL == arr)
{
printf("%s\n", strerror(errno));
}
else
{
for (i = 0; i < len; i++)
{
*(arr + i) = i * i;
}
for (i = 0; i < len; i++)
{
printf("%d ",*(arr + i));
}
// 動態開辟的內存,需要進行釋放,否則會導致內存泄露
free(arr);
// 手動將指針設為空指針,因為free沒有將其設為空指針的能力
// 如果沒有將其手動設為空指針,且別別人使用該指針,則引起非法訪問內存的錯誤。
arr = NULL;
}
return 0;
}
開辟失敗時:
#include
#include
#include
#include
int main()
{
int* arr = (int*)malloc(INT_MAX);
if (NULL == arr)
{
printf("%s\n", strerror(errno));
}
else
{
// do something.
free(arr);
arr = NULL;
}
return 0;
}
2.3 動態內存分配函數calloc
2.3.1 calloc函數介紹
作 用:開始num個,每個大小為size個字節的一塊內存空間,并將每個字節設為0。Allocates an array in memory with elements initialized to 0.
函數原型:void *calloc( size_t num, size_t size );
參 數:num:Number of elements(申請元素的個數)
size:Length in bytes of each element(每個元素所占字節空間大小)
返 回 值:同malloc
注 意:與malloc函數的區別:申請成功后,把空間的每個字節進行初始化為0,然后再講空間的地址進行返回。
2.3.2 malloc和calloc函數的比較
#include
#include
#include
#include
int main()
{
int len = 10;
int* p1 = (int*)malloc(len * sizeof(int));
int* p2 = (int*)calloc(len, sizeof(int));
int i;
if (NULL == p1)
{
printf("%s\n", strerror(errno));
}
else
{
// do something
printf("malloc 返回指針空間的內容:隨機值:");
for (i = 0; i < len; i++) { printf("%d ",*(p1+i)); }
printf("\n");
free(p1);
p1 = NULL;
}
if (NULL == p2)
{
printf("%s\n", strerror(errno));
}
else
{
// do something...
printf("calloc 返回指針空間的內容:0: ");
for (i = 0; i < len; i++) { printf("%d ", *(p2 + i)); }
printf("\n");
free(p2);
p2 = NULL;
}
return 0;
}
2.4 動態內存分配函數realloc
2.4.1 realloc函數的說明
作 用:可以對動態申請的內存空間大小進行調整。
函數原型:Reallocate memory blocks.
參 數:memblock: Pointer to previously allocated memory block(將要修改的開辟內存塊的地址)
size: New size in bytes(調整后的內存塊的大小,新的大小)
返 回 值:同malloc和calloc,如果調整失敗,返回NULL,否則返回重新分配內存塊的空間地址(該地址有可能會發生變化)
注 意:
1. 會將原來內存空間上的數據移動到新的空間內存中。
2. 調整內存時存在兩種情況:
2.1. 原來申請的內存空間后面有足夠的空間用于調整內存。則會在原來內存空間的后面直接追加空間到指定大小,并返回原空間的地址。
原來空間的內容不發生變化。
2.2. 原來的空間沒有足夠的空間來滿足新的空間大小,則會在堆區重新找一塊連續空間來存放新的內存空間。
原空間的內容拷貝到新的空間中,釋放原來的內存空間,把新的內存空間的地址進行返回。
3. 用于接收realloc返回值的指針名最好不要和realloc的第一個參數名相同,即不要把原空間的地址作為接收realloc返回值,如:
建 議:p1 = (int*)realloc(p, newsize);p = p1;
不建議:p = (int*)realloc(p, newsize);
4. 釋放內存時:如上述例子
4.1. free(p1);p1 = NULL;
4.2. free(p);p = NULL;(推薦)
4.3. free(p1);p1 = NULL;free(p);p = NULL;(報錯,因為他們指向同一個空間,只能釋放一次,如注意2.1,
如果如注意2.2所示,realloc函數會把原來的內存塊釋放掉,所以不需要手動調用free(原來的內存地址))
5. 如果第一個參數為NULL,第二個參數為size,則相當于直接開辟size個字節的大小
此時realloc(NULL, size)等價于malloc(size);
2.4.2 realloc函數的使用
realloc的第一個參數不為NULL時:
#include
#include
#include
#include
int main()
{
int len = 5;
int add1 = 5;
int add2 = 50000;
int i;
// malloc函數申請空間
int* p = (int*)malloc(len * sizeof(int));
if (NULL == p)
{
printf("%s", strerror(errno));
return 0;
}
printf("malloc: %p\n", p);
for (i = 0; i < len; i++) { *(p + i) = i; }
// realloc函數申請增加少量的空間,原來內存空間后面有足夠的空間用于增加的空間
int* p1 = (int*)realloc(p, (len+add1) * sizeof(int));
if (NULL == p1)
{
printf("%s", strerror(errno));
return 0;
}
else
{
p = p1;
}
printf("realloc 增加小的內存空間: %p\n", p);
for (i = len; i < len + add1; i++) { *(p + i) = i; }
printf("會把原來的值給復制過來:\n");
for (i = 0; i < len + add1; i++) { printf("%d ", *(p + i)); }
// realloc函數申請增加大量的的空間,原來內存空間后面沒有足夠的空間用于增加的空間
// 需要重新找一塊足夠大的空間,足以存放原來和新增加的空間的大小,內存空間的地址則會變化
int* p2 = (int*)realloc(p, (len + add1 + add2) * sizeof(int)); printf("\n");
if (NULL == p2)
{
printf("%s", strerror(errno));
return 0;
}
else
{
p = p2;
}
printf("realloc 增加大的內存空間: %p\n", p);
for (i = len+add1; i < len + add1 + add2; i++) { *(p + i) = i; }
free(p);
p = NULL;
return 0;
}
realloc的第一個參數為NULL時:
#include
#include
#include
#include
int main()
{
int i;
int* p = (int*)realloc(NULL, 20);
if (NULL != p)
{
for (i = 0; i < 5; i++) { printf("%d ", *(p + i)); }
free(p);
p = NULL;
}
return 0;
}
第三:常見的內存錯誤
3.1 對開辟內存失敗且沒有檢查而進行解引用
錯誤寫法:
正確寫法:一定要對malloc、calloc、realloc函數的返回值進行判斷
3.2 對開辟的內存進行越界訪問
3.3 對非動態內存使用free函數
free函數只能對堆區上的空間進行釋放
而非動態內存函數開辟的空間不再堆區上,而在棧區或者靜態區上。
3.4 free開辟的部分空間
如果free釋放的空間的地址不是開辟空間的地址,都屬于free部分空間的范圍
編譯通過,運行報錯,因為會釋放不屬于開辟空間的內存。
3.5 free同一開辟空間多次
為了避免這種問題,應該遵守誰開辟,誰釋放,釋放后立馬將其設為NULL;
因為free(NULL);不起任何作用。
3.6 忘記free
如果對動態開辟的空間沒有free,則該空間,只有在程序結束時,才被回收。但是如果程序為一直運行狀態,且開辟空間為重復過程。則會持續開辟空間而不會被回收,因此就會導致可用的內存越來越少,即發生內存泄露的情況。
執行之前:
執行之后:
第四:經典易錯的面試題
4.1 非法訪問內存和內存泄露:
錯誤代碼:
#include
#include
#include
#include
void runtest(char* p) // 為實參的一份臨時拷貝
{
p = (char*)malloc(40);
} // 當函數調用結束后,創建的局部變量會自動銷毀
int main()
{
// 代碼運行時,會發生錯誤,或者崩潰
char* str = NULL;
runtest(str); // 參數為變量本身,指針變量也是變量,str以值的形式傳給形參p
strcpy(str, "hello, C/C++"); // 將字符串拷貝到空指針中,非法訪問內存
printf(str);
// 動態開辟的內存沒有釋放,會有內存泄露的問題
return 0;
}
正確代碼1:傳遞變量的地址
#include
#include
#include
#include
void runtest(char** p)
{
*p = (char*)malloc(40);
}
int main()
{
char* str = NULL;
runtest(&str); // 傳遞變量的地址
if (NULL != str)
{
strcpy(str, "hello, C/CPP");
printf(str);
free(str);
str = NULL;
}
return 0;
}
正確代碼2:將動態內存的地址返回
#include
#include
#include
#include
char* runtest(char* p) // 為實參的一份臨時拷貝
{
p = (char*)malloc(40);
return p;
}
int main()
{
char* str = NULL;
str = runtest(str); // 參數為變量本身,指針變量也是變量,str以值的形式傳給形參p
if (NULL != str)
{
strcpy(str, "hello, C/C++");
printf(str);
free(str);
str = NULL;
}
return 0;
}
4.2 返回棧區空間的地址
錯誤代碼:
#include
#include
#include
#include
char* runtest(void)
{
// 問題因為這是個局部變量,當函數調用結束后
// 雖然返回了該變量的指針,但是其所指向的空間的內容將會被銷毀
// 當函數調用后,對該地址進行訪問,就屬于非法訪問內存
// 數據經典的返回棧區空間的地址
char p[] = "hello c/cpp"; // p位于棧區
return p;
}
int main()
{
char* str = NULL;
str = runtest();
printf(str);
return 0;
}
正確代碼:將局部變量用static修飾,即將棧區的變量,修改為靜態區的變量
總結
以上是生活随笔為你收集整理的c语言malloc引用类型作参数,C语言动态内存函数的理解和总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言如何控制上位机界面大小,电机上位机
- 下一篇: 天津理工上机c语言报告5,天津理工大学c