当前位置:网站首页>C语言动态内存管理 —— 仅仅四个函数而已
C语言动态内存管理 —— 仅仅四个函数而已
2022-07-16 01:37:00 【龙兆万】
1.为什么存在动态内存分配
首先我们已经掌握的内存开辟方式有:定义变量、数组等。其空间都是在栈上,栈上的空间有以下几个特征:
- 开辟的空间大小是固定的
- 数组在声明的时候,必须指定数组的大小,这会影响内存分配给数组的空间
当我们学习实用动态内存之后,就可以不考虑上面的情况。
2.动态内存函数介绍
2.1 malloc
这是 C 语言提供的一个动态内存开辟的函数,其函数声明为:
void* malloc (size_t size);这个函数向内存申请一块连续并且可用的空间,并返回此空间的地址(指针)。
那么 malloc 函数有以下几个规则:
- 如果开辟成功,则返回一个开辟好的空间的指针(无类型指针)
- 如果开辟失败,则返回一个空指针
- 返回值(指针)的类型是 void* ,所以使用 malloc 开辟空间时要配合强制类型转换
- 如果参数 size 为 0 ,此时的 malloc 的行为标准是未定义的,具体取决于编译器
接下来我们来使用 malloc 函数:
#include <stdlib.h> #include <string.h> #include <errno.h> #include <stdio.h> int main() { int* arr = NULL; arr = (int*)malloc(40);//arr 指针指向一块 40 字节的空间地址 if (arr == NULL)//如果是返回值是空指针 { printf("%s\n", strerror(errno));//搭配 string 类函数 return 1; } for (int i = 0; i < 10; i++) { arr[i] = i; printf("%d ", arr[i]); } return 0; }
2.2 calloc
此函数也是 C 语言提供的动态内存函数,原型如下:
void* calloc (size_t num,size_t size);参数的意义为:开辟 num 个大小为 size 的空间。
此函数与 malloc 的区别在于,会在返回空间地址之前,把空间的每个字节都初始化为 0 。
#include <stdlib.h> #include <string.h> #include <errno.h> #include <stdio.h> int main() { int* arr = NULL; arr = (int*)calloc(10, sizeof(int)); if (arr == NULL) { printf("%s\n", strerror(errno)); return 1; } for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
2.3 realloc
realloc 函数让动态内存管理更加灵活。有时候我们会觉得使用 malloc 或 calloc 申请的空间小了或者大了,那么 realloc 就能对申请的空间进行重新调整。其函数原型如下:
void* realloc (void* ptr,size_t size);其参数以及返回值的意义为:
- ptr 是要调整的内存地址
- size 是调整之后的空间大小(注意是调整之后的空间大小)
- 返回值为调整之后的空间地址
realloc 对重新开辟的空间有两种做法:
- 原有空间之后有足够大的空间,那么扩展的空间会直接追加上去
- 原有空间之后没有足够大的空间,那么 realloc 就会在堆区上重新找一块合适的连续空间
那么对于 realloc 的使用也非常简单:
#include <stdlib.h> #include <string.h> #include <errno.h> #include <stdio.h> int main() { int* arr = NULL; arr = (int*)calloc(5, sizeof(int)); if (arr == NULL) { printf("%s\n", strerror(errno)); return 1; } int* tmp = (int*)realloc(arr, 10*sizeof(int));//现在要调整为 10 个字节的大小 if (tmp == NULL) { printf("%s\n", strerror(errno)); return 1; } else arr = tmp; return 0; }
2.4 free
其实,在没有介绍 free 之前,上面的代码都是"错误"的。free 也是 C 语言提供的函数,是专门用来做动态内存的释放和回收的。其函数原型如下:
void free(void* ptr);free 函数有以下几个规则:
- 如果参数 prt 指向的空间不是动态开辟的,那 free 函数的行为是未定义的
- 如果参数 ptr 是空指针,那么函数什么都不做
我们补全上面的代码:
malloc:
#include <stdlib.h> #include <string.h> #include <errno.h> #include <stdio.h> int main() { int* arr = NULL; arr = (int*)malloc(40);//arr 指针指向一块 40 字节的空间地址 if (arr == NULL)//如果是返回值是空指针 { printf("%s\n", strerror(errno));//搭配 string 类函数 return 1; } for (int i = 0; i < 10; i++) { arr[i] = i; printf("%d ", arr[i]); } free(arr); arr = NULL; return 0; }realloc:
#include <stdlib.h> #include <string.h> #include <errno.h> #include <stdio.h> int main() { int* arr = NULL; arr = (int*)calloc(5, sizeof(int)); if (arr == NULL) { printf("%s\n", strerror(errno)); return 1; } int* tmp = (int*)realloc(arr, 10*sizeof(int));//现在要调整为 10 个字节的大小 if (tmp == NULL) { printf("%s\n", strerror(errno)); return 1; } else arr = tmp; free(arr); arr = NULL; return 0; }calloc:
#include <stdlib.h> #include <string.h> #include <errno.h> #include <stdio.h> int main() { int* arr = NULL; arr = (int*)calloc(10, sizeof(int)); if (arr == NULL) { printf("%s\n", strerror(errno)); return 1; } for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } free(arr); arr = NULL; return 0; }
3. 常见的动态内存错误
3.1 对空指针的解引用操作
#include <stdlib.h> void test() { int* p = (int*)malloc(INT_MAX / 4); *p = 20;//如果p的值是NULL,就会有问题 free(p); } int main() { test(); return 0; }
3.2 对动态开辟空间的越界访问
#include <stdlib.h> void test() { int i = 0; int* p = (int*)malloc(10 * sizeof(int)); if (NULL == p) { exit(EXIT_FAILURE); } for (i = 0; i <= 10; i++) { *(p + i) = i;//当i是10的时候越界访问 } free(p); } int main() { test(); return 0; }这种情况编译器是不会报错的,因为我们在使用数组时经常存在这个问题。但是在运行时会发生错误。
3.3 对非动态开辟的空间使用 free 释放
#include <stdlib.h> void test() { int a = 10; int* p = &a; free(p);//ok? } int main() { test(); return 0; } 这种情况也会出现运行错误。
3.4 使用 free 释放动态内存的一部分
#include <stdlib.h> void test() { int* p = (int*)malloc(100); p++; free(p);//p不再指向动态内存的起始位置 } int main() { test(); return 0; }
3.5 对用一块内存多次释放
#include <stdlib.h> void test() { int* p = (int*)malloc(100); free(p); free(p);//重复释放 } int main() { test(); return 0; }
3.6 动态开辟内存忘记释放
这种情况就像我们上面未介绍 free 之前时一样。
#include <stdlib.h> void test() { int* p = (int*)malloc(100); if (NULL != p) { *p = 20; } } int main() { test(); while (1); }虽然编译器不会报错,运行也不会报错,但是不释放内存是不可取的坏习惯。
4. 经典笔试题
4.1 题目一
#include <stdio.h> #include <stdlib.h> void GetMemory(char* p) { p = (char*)malloc(100); } void Test(void) { char* str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } int main() { Test(); return 0; }
4.2 题目二
#include <stdio.h> #include <stdlib.h> char* GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char* str = NULL; str = GetMemory(); printf(str); } int main() { Test(); return 0; }
4.3 题目三
#include <stdio.h> #include <stdlib.h> void GetMemory(char** p, int num) { *p = (char*)malloc(num); } void Test(void) { char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } int main() { Test(); return 0; }
4.4 题目四
#include <stdio.h> #include <stdlib.h> void Test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if (str != NULL) { strcpy(str, "world"); printf(str); } } int main() { Test(); return 0; }
![]()
边栏推荐
- 【对象头】查看对象占用字节
- JupyterLab安装
- Pinctrl subsystem and GPIO subsystem
- . Miniapi of net7 (special article):preview6 caching and stream limiting
- 长安链介绍-01
- Gao fushuai in unit testing, pytest framework (end) test report
- A limit question
- Openeuler knowledge: sig
- 1523. Count the number of odd numbers within the interval
- 数据库定时备份linux篇
猜你喜欢
![[idea] add VM options to idea](/img/a4/807408659d28d7491bcf606ec9d25f.png)
[idea] add VM options to idea

Community summit pulsar summit old golden peak conference topic highlights exposure!

JVM tuning practice (detailed version)

Hidden Markov model (HMM)
![[object header] view the bytes occupied by the object](/img/e5/226fa5858e1f4969a9d7cf8ff3c323.png)
[object header] view the bytes occupied by the object

【对象头】查看对象占用字节

Excel-VBA 快速上手(七、获取单元格对象)

Reverse learning notes (I)

Interview frequency: how does MySQL ensure high availability?

Résolution du format d'image
随机推荐
JVM introduction
Analysis of common interview questions in MySQL
Free SSL certificate application and deployment practice
Openeuler knowledge: repo
Résolution du format d'image
Flink基础记录补充
Xunwei Godson development board domestic dual core 64 bit loognix system dual Gigabit Ethernet more interfaces
HDOJ-2057(A + B Again)
MySQL original field to hump naming
Image format analysis
Summarize the differences between i++ and i++
My second anniversary of creation
Opensource knowledge: embedded software list
openEuler 知:官方社区
第50篇-某查查请求头参数分析【2022-07-14】
【微信小程序】基本橫向、纵向滚动Scroll-view(95/100)
Binary tree traversal
Redis数据结构实战演练,看看微博、微信、购物车、抽奖小程序是如何使用的?
Flink basic record supplement
Transformation of brushless motor on disk













