动态内存分配
在创建变量的时候实际上就是在开辟分配空间
1 2
| int val = 0; int arr[5] = { 0 };
|
创建变量的开辟空间方式有两个特点:
某些情况,需要的空间大小在程序运行时才知道
C语言提供了动态内存开辟,让开发者自行申请和释放空间
malloc 和 free
malloc
一个用于动态开辟内存空间的函数,包含在头文件<stdlib.h>中,语法格式如下
1
| void* malloc (size_t size);
|
开辟成功,返回一个指向开辟好空间的指针
开辟失败,返回 NULL
返回值类型是 void* , 使用时需操作者根据需求决定怎么使用
size 如果为 0, malloc 的行为是标准未定义的,由编译器决定
1 2 3 4 5 6 7 8 9 10 11 12
| int* p = (int*)malloc(20); if (!p) { perror("malloc"); return 1; }
for (int i = 1;i <= 5;i++) { *(p + i) = i; }
|

可以看到申请成功了

成功赋值
free
向C语言申请了内存空间,在使用完成后就需要释放和回收
C语言提供了 free 函数用于动态空间的释放和回收,也包含在头文件<stdlib.h>中
需要注意的是,传给 free 的是动态开辟的内存空间的起始地址
1 2 3 4 5 6 7 8 9 10 11 12 13
| int* p = (int*)malloc(20); if (!p) { perror("malloc"); return 1; }
for (int i = 0;i < 5;i++) { *p++ = i; }
free(p);
|

这里 p 自加后已经越过了 malloc 动态开辟的空间所以报错了
calloc 和 realloc
calloc
包含在头文件<stdlib.h>中,语法格式如下
1
| void* calloc (size_t num, size_t size);
|
- 为 num 个大小为 size 的元素开辟一块空间,并把空间的每个字节初始化为 0
1 2 3 4 5
| int* p = (int*)calloc(5, sizeof(int)); for (int i = 0;i < 5;i++) { printf("%d ", *(p + i)); }
|

如果要求申请的空间初始化,就可以用 calloc
realloc
包含在头文件<stdlib.h>中,语法格式如下
1
| void* realloc (void* ptr, size_t size);
|
对动态开辟内存空间的大小进行调整
ptr 是要调整的动态开辟内存空间的起始地址
size 调整后的内存空间的大小
返回值是调整之后内存空间的起始地址
调整后会将原空间的数据移动到新的空间中
1 2 3 4 5 6 7 8 9
| void* p = malloc(5 * sizeof(int)); if (!p) { perror("malloc"); return 1; }
p = realloc(p, 10 * sizeof(int));
|
realloc 调整空间大小有三种情况:
若原有空间后有足够大的空间,就在原有空间上向后进行扩展


若原有空间不够大,就找一块尚未分配且足够大的空间,将原空间的数据拷贝过去,
返回这一块新的空间的起始地址


调整失败,返回 NULL
需要注意的是, realloc 在调整空间时,若是重新开辟一块新的内存空间,会自行将原来的
空间释放掉。若有指针指向原有空间需将该指针置空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| int* p = (int*)calloc(5, sizeof(int)); if (!p) { perror("malloc"); return 1; }
int* p2 = (int*)realloc(p, 10 * sizeof(int)); if (!p2) { perror("realloc"); return 1; }
for (int i = 0;i < 10;i++) { *(p2 + i) = i; }
for (int i = 0;i < 10;i++) { printf("%d ", *(p2 + i)); }
free(p); p = NULL;
|

realloc 也可以实现和 malloc 一样的功能,给 ptr 传 NULL 就可以了
常见的动态内存错误
对 NULL 指针解引用操作
1 2 3
| int* p = (int*)malloc(INT_MAX ); *p = 20; free(p);
|

对动态开辟内存空间的越界访问
1 2 3 4 5 6
| int* p = (int*)malloc(5 * sizeof(int)); for (int i = 0;i <= 5; i++) { *(p + i) = i; } free(p);
|

对非动态开辟内存进行释放
1 2 3
| int a = 0; int* p = &a; free(p);
|

使用 free 释放一块动态开辟内存的一部分
1 2
| int* p = (int*)calloc(4,sizeof(int)); free(p + 1);
|

对同一块动态内存多次释放
1 2 3
| int* p = (int*)malloc(4); free(p); free(p);
|

动态内存开辟忘记释放(内存泄漏)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void test() { int* p = (int*)malloc(100); if (NULL != p) { *p = 20; } }
int main() { test(); while (1); return 0; }
|
!
在这串代码中, test() 中开辟的空间没有释放,就造成了内存泄漏
动态开辟的内存空间一定要释放,并且正确的释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; }
|
这段代码会运行崩溃

这里是传值调用,数是 char* 类型,传的参数也是 char* 类型,属于是传值调用,形参
的操作影响不了实参,str 仍然是 NULL , 这里对 NULL 解引用了
同时,也没有对动态
开辟的内存进行释放
解决方法就是使用传址调用,而保存 char* 这个一级指针变量的变量就是 char** 这
个二级指针,再加一部释放内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void GetMemory(char** p) { *p = (char*)malloc(100); } void Test(void) { char* str = NULL; GetMemory(&str); strcpy(str, "hello world"); printf(str); free(str); }
int main() { Test(); return 0; }
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| char* GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char* str = NULL; str = GetMemory(); printf(str); }
int main() { Test(); return 0; }
|

函数执行完后,局部数组 p 所在的栈空间被系统自动回收,指针 str 指向了被回
收的空间,因此成为了野指针
解决方法是可以在函数 GetMemory()使用动态开辟内存,动态开辟内存在未手动释
放的情况下只会在程序结束运行后由系统自动释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| char* GetMemory(void) { char* p = (char*)malloc(12); strcpy(p, "hello world"); return p; } void Test(void) { char* str = NULL; str = GetMemory(); printf(str); }
int main() { Test(); return 0; }
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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; }
|
这段代码的能够正常执行函数,但是在 Test() 中,使用完动态开辟内存后应该进行释
放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void GetMemory(char** p, int num) { *p = (char*)malloc(num); }
void Test(void) { char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); free(str); str = NULL; }
int main() { Test(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; }
|

这一段代码虽然能执行,但本质上属于非法访问内存, str 所指向的内存空间被释放
了,逻辑上讲已经没有对 str 指向内存空间的使用权了, 但这块内存空间仍然存
在,str 并未置空,对他进行使用属于非法访问
在释放动态内存空间后要将指向这片空间的指针变量置空
1 2 3 4 5 6 7 8 9 10 11 12
| void Test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); str = NULL; if (str != NULL) { strcpy(str, "world"); printf(str); } }
|
柔性数组
C99中,结构中的最后一个元素允许是未知大小的数组,这叫柔性数组
1 2 3 4 5
| struct S { char c; int arr[]; };
|
柔性数组的特点
柔性数组的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
struct S* p = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int)); if (!p) { perror("malloc"); return 1; }
for (int i = 0;i < 5;i++) { p->arr[i] = i; }
for (int i = 0;i < 5;i++) { printf("%d ", p->arr[i]); } printf("\n");
struct S* ps = (struct S*)realloc(p, sizeof(struct S) + 10 * sizeof(int)); if (!ps) { perror("realloc"); return 1; } p = ps; for (int i = 0;i < 10;i++) { ps->arr[i] = i; }
for (int i = 0;i < 10;i++) { printf("%d ", ps->arr[i]); } free(p);
|


指针的方式实现柔性数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| struct S2 { int c; int* arr; };
struct S2* p =(struct S2*)malloc(sizeof(struct S2)); if (!p) { perror("malloc"); return 1; }
p->arr = (int*)malloc(5 * sizeof(int)); if (!p->arr) {
perror("malloc"); return 1; }
for (int i = 0;i < 5;i++) { p->arr[i] = i; }
for (int i = 0;i < 5;i++) { printf("%d ",p->arr[i]); } printf("\n");
int* ps = (int*)realloc(p->arr,10 * sizeof(int)); if (!ps) { perror("realloc"); return 1; }
p->arr = ps; for (int i = 0;i < 10;i++) { p->arr[i] = i; }
for (int i = 0;i < 10;i++) { printf("%d ", p->arr[i]); }
free(p->arr); free(p);
|

