C 內存管理函數
C 語言為內存的分配和管理提供了幾個函數。這些函數可以在 <stdlib.h> 頭文件中找到。
序號 | 函數和描述 |
---|---|
1 | void calloc(int num, int size); 在內存中動態地分配 num 個長度為 size 的連續空間,并將每一個字節都初始化為 0。所以它的結果是分配了 numsize 個字節長度的內存空間,并且每個字節的值都是0。 |
2 | void free(void *address); 該函數釋放 address 所指向的內存塊,釋放的是動態分配的內存空間。 |
3 | void *malloc(int num); 在堆區分配一塊指定大小的內存空間,用來存放數據。這塊內存空間在函數執行完成后不會被初始化,它們的值是未知的。 |
4 | void *realloc(void *address, int newsize); 該函數重新分配內存,把內存擴展到 newsize。 |
C語音里面的內存劃分
- 棧區(棧內存,存放局部變量,自動分配和釋放,里面函數的參數,方法里面的臨時變量)
- 堆區(動態內存分配,C語音里面由程序員手動分配),最大值為操作系統的80%
- 全局區或靜態區
- 常量區(字符串)
- 程序代碼區
靜態與動態內存分配
在程序運行過程中,動態指定需要使用的內存大小,手動釋放,釋放之后這些內存還可以被重新使用。
靜態內存分配,分配內存大小的是固定,產生的問題:
1.很容易超出棧內存的最大值
2.為了防止內存不夠用會開辟更多的內存,容易浪費內存。
動態內存分配,在程序運行過程中,動態指定需要使用的內存大小,手動釋放,釋放之后這些內存還可以被重新使用
棧溢出
下面的代碼會導致棧溢出
void main(){
//屬于靜態內存分配,分配到棧里面,Window里面每一個應用棧大概是2M,大小確定。與操作系統有關。
int a [1024 * 1024 * 10 * 4];
}
該靜態內存定義為40M,而Window里面每一個應用棧大概是2M,超出了范圍, 會報stack overflow錯誤 。
動態內存分配與釋放
//堆存分配,40M
//參數:字節 KB M 10M 40M
//開辟
int* p1 = (int*)malloc(1024*1024*10*sizeof(int));
//釋放
free(p1);
通過動態內存分配來動態指定數組的大小
在程序運行過長中,可以隨意的開辟指定大小的內存,以供使用,相當于Java中的集合
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//創建一個數組,動態指定數組的大小
void main() {
//靜態內存分配創建數組,數組的大小是固定的
//int i = 10;
//int a[i];
int len;
printf("輸入數組的長度:");
scanf("%d", &len);
//開辟內存,大小內存len * 4 字節
int* p = (int*)malloc(len * sizeof(int));//p:數組的首地址
int i = 0;
for (; i < len; i++) {
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
//手動釋放內存
free(p);
getchar();
}
結果輸出:
41,0x513f48
67,0x513f4c
34,0x513f50
0,0x513f54
69,0x513f58
24,0x513f5c
重新分配realloc
重新分配內存的兩種情況:
縮小內存,縮小的那一部分數據會丟失
擴大內存,(連續的)
1.如果當前內存段后面有需要的內存空間,直接擴展這段內存空間,realloc返回原指針
2.如果當前內存段后面的空閑字節不夠,那么就使用堆中的第一個能夠滿足這一要求的內存塊,將目前的數據復制到新的位置,并將原來的數據庫釋放掉,返回新的內存地址
3.如果申請失敗,返回NULL,原來的指針仍然有效
void main() {
int len;
printf("第一次輸入數組的長度:");
scanf("%d", &len);
//開辟內存,大小內存len * 4 字節
int* p = (int*)malloc(len * sizeof(int));//p:數組的首地址
int i = 0;
for (; i < len; i++) {
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
int addLen;
printf("輸入數組增加的長度:");
scanf("%d", &addLen);
int* p2 = (int*)realloc(p, sizeof(int) * (len + addLen));
if (p2 == NULL) {
printf("重新分配失敗......");
}
printf("------------新數組-------------------\n");
//重新賦值
i = 0;
for (; i < len + addLen; i++) {
p2[i] = rand() % 200;
printf("%d,%#x\n", p2[i], &p2[i]);
}
//手動釋放內存 p2釋放內存 p也會釋放,因為給p2分配內存的時候要么p已經釋放,要么p2、p指向統一地址區域
if (p2 != NULL) {
free(p2);
p2 = NULL;
}
getchar();
}
結果輸出:
第一次輸入數組的長度:5
41,0x5e4ad8
67,0x5e4adc
34,0x5e4ae0
0,0x5e4ae4
69,0x5e4ae8
輸入數組增加的長度:5
------------新數組-------------------
124,0x5e4ad8
78,0x5e4adc
158,0x5e4ae0
162,0x5e4ae4
64,0x5e4ae8
105,0x5e4aec
145,0x5e4af0
81,0x5e4af4
27,0x5e4af8
161,0x5e4afc
內存分配的幾個注意細節
1.不能多次釋放
2.釋放完之后(指針仍然有值),給指針置NULL,標志釋放完成
3.內存泄露(p重新賦值之后,再free,并沒有真正釋放內存)
避免內存泄漏
p重新賦值之前先free
內存泄漏寫法:
void main(){
//40M
int* p1 = malloc(1024 * 1024 * 10 * sizeof(int));
//free(p1);
//p1 = NULL;
//錯誤,沒有立即釋放內存
printf("%#x\n",p1);
//80M
p1 = malloc(1024 * 1024 * 10 * sizeof(int) * 2);
free(p1);
p1 = NULL;
getchar();
}
打開任務管理器,看到有40M內存泄漏。
正確寫法:
void main(){
//40M
int* p1 = malloc(1024 * 1024 * 10 * sizeof(int));
free(p1);
p1 = NULL;
printf("%#x\n",p1);
//80M
p1 = malloc(1024 * 1024 * 10 * sizeof(int) * 2);
free(p1);
p1 = NULL;
getchar();
}
打開任務管理器,看到內存只有0.3M,正常。
微信號kpioneer