C语言程序设计中动态内存分配如何实现

嵌入式技术

1330人已加入

描述

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语言动态申请内存的相关内容,动手尝试验证一下上述问题。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分