动态内存分配

  调用动态内存分配的函数前需要先引入malloc.h

1
#include <malloc.h>

malloc

  全称是memory allocationmalloc函数可以在动态储存区的堆内存中分配一块指定大小的连续内存空间,调用该函数时传入需要分配的字节数,如果分配成功将返回一个指向新分配内存的指针,如果分配失败返回NULL

1
void* malloc(size_t size);

  如下代码在堆空间中分配了一块内存,并通过内存指针给该内存空间赋值;malloc在某些情况下会发生内存分配失败的情况,如内存大小不足、内存碎片化严重,所以通过int_pointer != NULL判断是否分配成功;在动态分配的内存使用完毕后,通过free函数释放内存,动态分配的内存如果不释放会发生内存泄漏,释放后再把指向被释放的内存地址的指针置空,避免该指针再次被程序调用,出现悬空指针问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 动态分配内存一个 int 类型变量大小的内存空间
// 然后将 void* 类型指针强制转换为 int* 型
int* int_pointer = (int*)malloc(sizeof(int));
// 判断内存空间是否分配成功
if (int_pointer != NULL) {
// 给指针指向的内存空间赋值
(*int_pointer) = 10;
// 将会输出 10
printf("value = %d\n", (*int_pointer));

// 释放动态分配的内存,避免内存泄露
free(int_pointer);
// 置空指针
int_pointer = NULL;
} else {
printf("内存空间分配失败");
}

  通过malloc在堆空间中创建了一个int类型数组,该程序将输出连续的5个堆空间中的内存地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define ARR_LENGTH 5

// 动态分配内存,并强制转换为 int 类型指针
int* arr_int_pointer = (int*)malloc(ARR_LENGTH * sizeof(int));

// 判断内存是否分配成功
if (arr_int_pointer != NULL) {
// 将指针作为 int 类型数组,输出每个元素的内存地址
for (int i = 0; i < ARR_LENGTH; i++) {
printf("*arr_int_pointer[%d] = %p\n", i, &arr_int_pointer[i]);
}

// 释放内存空间
free(arr_int_pointer);
// 置空指针
arr_int_pointer = NULL;
} else {
printf("内存分配失败");
}

  通过malloc同样也能创建一个结构体指针

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
// 声明结构体
struct Data{
int int_data;
float float_data;
};

// 动态分配内存,并强制转换为结构体类型指针
struct Data* data_pointer = (struct Data*)malloc(sizeof(struct Data));

// 判断内存是否分配成功
if (data_pointer != NULL) {
// 通过结构体指针修改结构体成员的值
data_pointer->int_data = 10;
data_pointer->float_data = 22.2f;

// 输出结构体成员的值
printf("int_data = %d, float_data = %.2f\n", data_pointer->int_data, data_pointer->float_data);

// 释放内存空间
free(data_pointer);
// 置空指针
data_pointer = NULL;
} else {
printf("内存分配失败");
}

calloc

  全称是contiguous alloccalloc函数可以在动态储存区的堆内存中分配一块连续的内存空间,空间的大小由函数的两个参数决定,分别可以理解为:代表元素数量和代表单个元素占用内存大小,调用该函数时传入这两个参数,如果分配成功将返回一个指向新分配内存的指针,并将该内存空间的每个字节初始化为零,如果分配失败返回NULL

1
void *calloc(size_t num_elements, size_t element_size);

  其实这个函数满操蛋的,因为完全可以用malloc来替代,如下,没有什么本质上的区别,只不过calloc分配的内存空间把每个字节都初始化为零

1
2
int* arr_int_pointer1 = (int*)calloc(ARR_LENGTH, sizeof(int));
int* arr_int_pointer2 = (int*)malloc(ARR_LENGTH * sizeof(int));

realloc

  用于更改由malloccalloc分配的内存块的大小,该函数需要两个参数,其中第一个代表需要修改的内存块的指针,第二个代表新的内存块大小;如果重新分配成功,则释放原内存块,并返回一个指向重新分配内存块的指针,重新分配失败则返回NULL,原内存块仍然有效。

1
void *realloc(void *ptr, size_t new_size);

  如果新的内存块大小,小于或等于原内存块的大小,那么原内存块可能被截断或保持不变,返回的指针可能与原地址相同,也可能是一个新的地址;如果新内存块的大小,大于原内存块的大小,那么原内存块的内容可能会被复制到新内存块中,并返回新的内存块的指针

1
2
3
4
5
6
7
8
#define ARR_LENGTH 5
// 原内存块大小 20
int* pointer1 = (int*)malloc(ARR_LENGTH * sizeof(int));
// 新内存块大小 20
int* pointer2 = (int*)realloc(pointer1, ARR_LENGTH * sizeof(int));

// pointer1 和 pointer2 有可能相同
printf("pointer1 = %p, pointer2 = %p\n", pointer1, pointer2);

1
2
3
4
5
6
7
8
#define ARR_LENGTH 5
// 原内存块大小 20
int* pointer1 = (int*)malloc(ARR_LENGTH * sizeof(int));
// 新内存块大小 24
int* pointer2 = (int*)realloc(pointer1, (ARR_LENGTH + 1) * sizeof(int));

// pointer1 和 pointer2 不相同
printf("pointer1 = %p, pointer2 = %p\n", pointer1, pointer2);

  如果第一个参数指定的指针是NULL,那么realloc将分配一个新的内存块,类似于malloc

1
2
3
4
5
6
7
8
9
10
11
12
#define ARR_LENGTH 5

// 声明一个空指针
int* null_pointer = NULL;

// 将空指针传入 realloc 将返回一个新的指定大小的内存块
int* new_arr_pointer = realloc(null_pointer, ARR_LENGTH * sizeof(int));

for (int i = 0; i < ARR_LENGTH; ++i) {
new_arr_pointer[i] = i;
printf("pointer = %p\n", &new_arr_pointer[i]);
}