C语言程序设计中,动态内存分配如何实现,需要注意哪些问题?
1、动态内存分配用malloc函数,他的函数原型
void * malloc (size_t size);
malloc有一个参数size,表示需要申请的内存空间大小,单位是字节。
- 分配的内存空间连续,如果没有空闲内存,可能分配失败
- 返回值为void*类型,也就是没有确定具体的数据类型,由用户自己决定,也就是需要强制数据类型转换
// 动态内存分配
#include < stdio.h >
#include < stdlib.h >
#define SIZE 5
void display(int *p, int n){
int i;
for(i = 0; i < n; i ++){
printf("%5dn", p[i]);
}
}
int main(){
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
for(int i = 0; i < SIZE; i ++){
p[i] = i;
}
display(p, SIZE);
free(p);
return 0;
}
案例中,分配了一个大小为SIZEsizeof(int)个字节的内存空间,强制转换为int类型,并由指针p指向该内存空间。
sizeof(int *); // 求出int *类型变量占据的内存大小
sizeof(int);// 求出int类型变量占据的内存大小
int *p; sizeof(*p); // 求出指针p所存放的地址占据的内存大小
假设int类型变量占据4个字节的内存,那么总共分配了20个字节的内存空间。
2、malloc分配的内存属于堆内存
所谓堆内存,他的生命周期与进程相同。
int* initArr(){
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
for(int i = 0; i < SIZE; i ++){
p[i] = i;
}
return p;
}
int main(){
int *p = initArr();
display(p, SIZE);
free(p);
return 0;
}
案例中,虽然动态内存是在函数initArr中申请到的,它的作用域应该是函数内部,但是在主函数main中依然可以使用这个内存,正是由于该内存属于堆内存,生命周期与进程相同,不会因为函数调用结束而释放。
同样的,正是因为该内存没有释放,才可以在函数display中继续使用。
3、内存释放函数free,函数原型
void free(void *ptr)
由于malloc申请的内存属于堆内存,生命周期较长,所以在使用完之后,如果后面的程序再也用不到该内存,就应该提前将其释放,释放malloc申请的内存用free函数。
free函数有一个参数,指向将要释放的内存块,所以是一个指针,没有返回值。
上面的案例中,在主函数返回之前,内存使用完之后,就直接释放了该内存。需要注意的是,如果后面还需要继续使用该内存,切不可提前释放。
int main(){
int *p = initArr();
free(p);
display(p, SIZE);
return 0;
}
这必然是一个错误的示例,如果提前释放了该内存,后面就找不到相应的内存,也就不能继续对该空间进行操作。
free(p);
p=NULL;
在释放动态内存之后,最好将原来的指针设置为空,防止指针p成为野指针被使用。
4、malloc数组的溢出
#define SIZE 5
#define N 7
int* initArr(){
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
for(int i = 0; i < N; i ++){
p[i] = i;
}
return p;
}
int main(){
int *p = initArr();
display(p, N);
free(p);
return 0;
}
案例打印的结果如下
0
1
2
3
4
5
6
是的,你没有看错,本来申请的是5个元素空间,这里打印了7个元素,而且没有报错。这就是mallo函数申请的内存与静态数组的区别,malloc申请的内存属于堆内存,虽然指定的内存只有5个元素大小,但是后面依然可以访问。
这是一个非常危险的操作,因为你不知道越界之后,对越界后的内存修改,会不会影响其他程序。
int a[5];
for(int i=0; i < 6; i ++){
a[i]=i;
}
在这个静态数组中,必然会报错,程序执行到后面的时候,可能执行失败,因为已经越界。静态数组存储在栈内存中,属于动态存储区,他不允许越界操作。
5、malloc函数可开辟的最大空间
malloc开辟的空间属于堆内存,静态数组属于栈内存,两者的最大容量存在差异。
#define SIZE 102400000000000
int *p = (int *)malloc(SIZE * sizeof(int));
if(!p) exit(-1);
在你的计算机上也许可以成功申请到这么大的内存,但是如果用静态数组,这个操作很可能失败。
#define SIZE 102400000000000
int a[SIZE];
a[0]=0;
printf("%dn", a[0]);
一般情况下,静态数组允许申请的最大连续空间,小于动态数组允许申请的最大连续空间。
6、calloc函数
void *calloc(size_t nitems, size_t size)
calloc函数与malloc函数功能相同,不同点是:calloc函数会对所有元素进行初始化,初始化为0。
calloc函数有两个参数,第一个参数是将要申请的元素个数,第二个参数是每个元素的内存大小。
int* initArr2(){
int *p = (int *)calloc(SIZE, sizeof(int));
if(!p) exit(-1);
return p;
}
int main(){
int *p = initArr2();
display(p, N);
free(p);
return 0;
}
案例中,申请了一个动态内存,并对该内存进行初始化,所有元素都是0,因此,在不给每个元素赋值的情况下,打印出来的全部是0。
calloc函数分配的内存也是堆内存,他与malloc相同,存在的问题也相同。
7、realloc函数
尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
void *realloc(void *ptr, size_t size)
第一个参数表示指向已经申请到的动态内存块,如果为空指针,则会重新分配一个新内存块。第二个参数表示新内存块的大小,可以比原来的内存块大,也可以比原来内存块小。
int* initArr3(int *p){
int *pnew = (int *)realloc(p, (SIZE + SIZE) * sizeof(int));
if(!pnew) exit(-1);
for(int i = 0; i < N; i ++){
pnew[i] = i;
}
return pnew;
}
int main(){
int *p = initArr3(NULL);
display(p, N);
free(p);
return 0;
}
传入的参数为NULL,可以重新分配一个内存块。
int main(){
int *p = initArr2();
display(p, N);
int *pnew = initArr3(p);
display(pnew, N);
// display(p, N);//此时原来的数组已经不存在
free(pnew);
return 0;
}
传入一个已经指向的内存,在原来的基础上进行扩大或者缩小,返回新内存的首地址。原来的内存将会被释放。
realloc函数与malloc、calloc函数类似,也会存在malloc函数类似的问题。
以上是C语言动态申请内存的相关内容,动手尝试验证一下上述问题。
评论
查看更多