C语言:动态内存分配函数

您所在的位置:网站首页 动态存储分配c语言 C语言:动态内存分配函数

C语言:动态内存分配函数

2024-07-14 01:08| 来源: 网络整理| 查看: 265

目录

1. 为什么要有动态内存分配

2. malloc 和 free

2.1 malloc

2.2 free

3. calloc 和 realloc

3.1 calloc

3.2 realloc

4. 常见动态内存错误

4.1 对NULL指针解引用

4.2 对动态开辟空间的越界访问

4.3 对非动态开辟内存使用free释放

4.4 使用free释放一块动态开辟内存的一部分

4.5 对同一块动态内存多次释放

4.6 动态开辟内存忘记释放(内存泄漏)

5. 动态内存经典试题解析

5.1 题目1

5.2 题目2

5.3 题目3

5.4 题目4

1. 为什么要有动态内存分配

在此之前,我们会通过创建局部变量,在栈区开辟空间。这种开辟空间的方式有两个缺陷:(1)开辟的空间大小是固定的;(2)数组长度指定后,不可调整。

但往往对于空间的需求,不仅仅是上述情况,有时候我们需要的空间在程序运行时才能知道,那数组在编译时开辟的固定空间就不能满足需求了。为此,C语言引入动态内存开辟,程序员可根据自身需求灵活开辟/扩大/缩小空间。

int main() { int a = 1; //在栈区开辟4个字节的空间 int arr1[10] = { 0 }; //在栈区开辟40个字节的连续空间 int arr2[] = {1,2,3}; //在栈区开辟14个字节的连续空间 char ch = "abcdef"; //在栈区开辟7个字节的连续空间 return 0; } 2. malloc 和 free 2.1 malloc

(1)定义:向内存申请一块连续可用的空间,并返回指向该空间的指针

void* malloc (size_t size);

(2)参数:字节大小,即需要申请的空间的大小

(3)返回:指针,指向所申请的连续空间的起始位置

(4)要点:

如果空间开辟成功,则返回指向开辟好的空间的指

 如果空间开辟失败,则返回NULL指针,因此该函数的返回值需要异常检查

 返回指针为void*,该函数并不知道开辟的空间具体是什么类型,需使用者明确

 如果参数为0,标准未定义,取决于编译器

(5)实现:

int main() { //开辟空间 int* p = (int*)malloc(10 * sizeof(int)); //判断返回是否为NULL指针 if(p == NULL) { perror("malloc"); return 1; } //赋值 int i = 0; for (i = 0; i < 10; i++) { *(p + i) = i; } //使用 for (int i = 0; i < 10; i++) { printf("%d ", *(p + i)); } return 0; } 2.2 free

C语言提供了另一个函数free,用于动态空间的释放和回收。

(1)定义:释放/回收指针所指向的动态空间。

void free (void* ptr);

(2)参数:指针,即指向动态空间的起始位置的指针

(3)返回:释放/回收动态空间

(4)要点:

 free函数释放/回收malloc/calloc/realloc开辟的动态空间如果参数ptr指向的空间不是动态开辟的,free函数的行为是未定义的如果参数ptr为NULL指针,则free函数什么也不做。free函数仅释放指针所指向的动态空间,并不会改变指针。需要注意的是:指针未改变,指针所指向的空间被销毁,指针会变成野指针,因此指针需置为NULL。

(5)实现:

int main() { //开辟空间 int* p = (int*)malloc(10 * sizeof(int)); //判断返回是否为NULL指针 if(p == NULL) { perror("malloc"); return 1; } //赋值 int i = 0; for (i = 0; i < 10; i++) { *(p + i) = i; } //使用 for (int i = 0; i < 10; i++) { printf("%d ", *(p + i)); } //释放空间 free(p); //p指针置为NULL p = NULL; return 0; } 3. calloc 和 realloc 3.1 calloc

(1)定义:向内存申请一块连续可用的空间,初始化为0,并返回指向该空间的指针。

void* calloc (size_t num, size_t size);

(2)参数:num为元素个数,size为元素大小

(3)返回:指针,指向所申请的连续空间的起始位置

(4)要点:

函数功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0calloc函数与malloc函数的区别在于:在malloc函数功能基础上,对所开辟的空间进行初始化,初始化值为0

(5)实现:

int main() { //开辟空间 int* p = (int*)calloc(4, sizeof(int)); //判断返回是否为NULL指针 if (p == NULL) { perror("calloc"); return 1; } //使用 for (int i = 0; i < 4; i++) { printf("%d ", *(p + i)); } //释放空间 free(p); //指针置为NULL p = NULL; return 0; }

3.2 realloc

realloc函数相较于上面的函数更加灵活,除了开辟动态空间外,还能扩大/缩小已开辟的空间。

(1)定义:

void* realloc (void* ptr, size_t size);

(2)参数:ptr为需要调整的内存地址,size为调整后的动态空间大小

(3)返回:调整后动态空间的起始位置

(4)要点:

当ptr为NULL指针时,realloc函数和malloc函数功能一样,可用于开辟新的动态空间。当realloc函数调整内存空间时会存在两种情况:1)原有空间之后有足够大的空间:则扩展内存直接在原有空间上追加,原有空间数据不发生变化,返回旧地址;2)原有空间之后没有足够大的空间:在堆区找一块空间大小合适的连续空间,拷贝旧空间数据至新空间,释放旧空间,返回指向新空间的地址。 如果调整内存空间失败,realloc函数返回NULL指针。因此对于realloc函数调整的内存空间,不应该用旧地址接收,避免内存空间调整失败,导致已有数据被销毁。

(5)实现: 

int main() { //开辟动态空间 int* p = (int*)malloc(10 * sizeof(int)); //判断返回是否异常 if (p == NULL) { perror("malloc"); return 1; } //赋值 for (int i = 0; i < 10; i++) { *(p + i) = i; } //打印 for (int i = 0; i < 10; i++) { printf("%d ", *(p + i)); } printf("\n"); //空间不够,扩展空间 int * ptr = (int*)realloc(p, 15 * sizeof(int)); if (ptr == NULL) { perror("realloc"); return 1; } p = ptr; //新地址不为NULL指针的时候,赋值给旧指针变量 ptr=NULL; //为新增的动态空间赋值 for (int i = 0; i < 5; i++) { *(p + 10 + i) = i; } //打印 for (int i = 0; i < 15; i++) { printf("%d ", *(p + i)); } free(p); p = NULL; return 0; }

4. 常见动态内存错误 4.1 对NULL指针解引用

malloc/calloc/realloc返回值可能为NULL指针,直接解引用会有问题,因此需要对动态空间开辟函数的返回值进行检查,非空才可解引用。

int main() { int * p = (int*)malloc(10 * sizeof(int)); *p = 20; //返回值可能为NULL指针 return 0; } 4.2 对动态开辟空间的越界访问 int main() { int* p = (int*)malloc(10 * sizeof(int)); if (NULL == p) { perror("malloc"); return 1; } for (int i = 0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3