1 静态开辟

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
#include <stdio.h>
#include <unistd.h> // 小写的

// 函数进栈 定义一个int arr[5]; 定义一个 int i; (静态的范畴)

// 进栈
void staticAction() {
int arr[5]; // 静态开辟 栈区 (栈成员)

for (int i = 0; i <5; ++i) {
arr[i] = i;
printf("%d, %p\n", *(arr + i), arr + i);
}
} // 函数的末尾会弹栈(隐士):执行完毕会弹栈 会释放所有的栈成员

// 2.静态开辟。
int mainT3() {
// int 4 * 10 = 40字节
// int arr[10 * 1024 * 1024]; // 10M * 4 = 40M 会栈溢出

// int arr[1 * 1024 * 1024]; 会栈溢出

int arr[(int)(0.2 * 1024 * 1024)]; // 不会栈溢出

// 栈区:占用内存大小 最大值: 大概 2M 大于2M会栈溢出 平台有关系的
// 堆区:占用内存大小 最大值: 大概80% 40M没有任何问题,基本上不用担心 堆区很大的
// 大概80%: Windows系统 给我们的编译器给予的空间 的 百分之百八十

while (9) {
sleep(100);
staticAction(); // 调用开辟20
}

return (0);
}

2 动态开辟

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // 小写的

/// 函数进栈 定义一个int arr[5]; 定义一个 int i; (静态的范畴)
// malloc 在堆区开辟的内存空间 , (动态的范畴)
// C的开发过程中,不能出现,野指针,悬空指针
void dynamicAction() {
int * p; // 野指针 没有地址的,空的

// void * 可以任意转变 int* double *
int * arr = malloc(1 * 1024 * 1024); // 堆区开辟 4M

printf("dynamicAction函数,arr自己的内存地址:%p,堆区开辟的内存地址:%p\n", &arr, arr);

// 堆区开辟的空间,必须释放
free(arr); // 释放掉
// 内存释放掉之后要将栈指针指向null,否则成悬空指针
arr = NULL; // 重新指向一块内存地址00000

printf("dynamicAction函数2 堆区开辟的内存地址:%p\n", arr); // 悬空指针
}

int main() {
dynamicAction();
return 0;
}

3 动态开辟的使用的场景

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
#include <stdio.h>
#include <stdlib.h>

// 动态开辟的使用的场景
int mainT2() {
// 静态开辟的内存空间大小,是不能修改的,如果不需要动态修改空间大小,当然使用栈区
//【尽量使用 静态开辟的,如果实在是需要动态改变,才使用下面】
// int arr [6];

// =================================== 下面是 动态开内存的使用场景
// 开辟的空间想要变化, 动态范畴

int num;
printf("请输入数的个数:");

// 获取用户输入的值
scanf("%d", &num);

// 动态开辟 用户输入的值 空间的大小 【堆区】
int * arr = malloc(sizeof(int) * num);
// int arr2 [] == int * arr 一样的了

int print_num;
// 循环接收
for (int i = 0; i < num; ++i) {
printf("请输入第%d个的值:", i);

// 获取用户输入的值
scanf("%d", &print_num);

arr[i] = print_num;
printf("每个元素的值:%d, 每个元素的地址:%p\n", *(arr + i), arr + i);
}

// for 循环打印
for (int i = 0; i < num; ++i) {
printf("输出元素结果是:%d\n", arr[i]); // arr[i] 隐士 等价与 * (arr + i)
}

getchar();
return 0;
}

4 动态开辟之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
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <stdio.h>
#include <stdlib.h>

// 动态开辟之realloc
int main() {

int num;
printf("请输入个数");
// 获取用户输入的值
scanf("%d", &num);

// 5个值
int * arr = (int *) malloc(sizeof(int) * num);
for (int i = 0; i < num; ++i) {
arr[i] = (i + 10001); // arr[i]的内部隐士 == *(arr+i)
}
printf("开辟的内存指针: %p\n", arr);

// ================================= 在堆区开辟新的空间 加长空间大小

// realloc 为什么一定要传入 arr指针,为什么要传总大小
// 防止申请失败的时候进行复制内容

// 新增
int new_num;
printf("请输入新增加的个数");
scanf("%d", &new_num);

// 原来的大小4 + 新增加的大小4 = 总大小 8
// void *realloc (void *前面开辟的指针, size_t总大小);
int * new_arr = (int *) realloc(arr, sizeof(int) * (num + new_num));

if (new_arr) { // new_arr != NULL 我才进if 【非0即true】
int j = num; // 4开始
for (; j < (num + new_num); j++) { // 5 6 7 8
arr[j] = (j + 10001);
}

printf("新 开辟的内存指针: %p\n", new_arr);

// 后 打印 内容
for (int i = 0; i < (num + new_num); ++i) {
printf("新 元素的值:%d, 元素的地址:%p\n",
*(arr + i),
(arr + i)
);
}
}

// 我已经释放
free(new_arr);
new_arr = NULL;

// 1000行代码
// 。。。

// 重复释放/重复free VS会奔溃, CLion会优化(发现不奔溃) [错误的写法]
/*free(new_arr);
new_arr = NULL;*/

// 必须释放【规则】
/*if (arr) {
free(arr); // 如果不赋值给NULL,就是悬空指针了
arr = NULL;
}*/

if (new_arr) { // new_arr != NULL 进去if, 重新开辟的堆空间是成功的
free(new_arr);
new_arr = NULL;
arr = NULL; // 他还在指向那块空间,为了不出现悬空指针,指向NULL的空间
} else { // 重新开辟的堆空间是失败的
free(arr);
arr = NULL;
}

return 0;
}