C语言的malloc分配注意事项

引子

我一直以来都很喜欢C语言——纯粹的C语言,不加任何C++。这是一种纯粹的开发过程,我是内存的巫师。

我在YouTube关注了一个开发者,叫做Tsoding。他用C语言实现了很多有意思的东西——音乐可视化器、BPE算法等等。

然而,许久不用,我已经快忘记了如何进行基本的内存管理,这篇文章就来记叙一下我写Side Project过程中遇到的一些问题。

这次我的Side Project是动态表(Dynamic Table, Dynamic Array),这是《算法导论》一书中所遇到的第一个数据结构(或许)。

嵌套结构体的malloc

嵌套结构体需要一层一层分配空间,比如:

1
2
3
4
5
6
7
8
9
typedef struct {
int actual_value;
char description;
} Node;

typedef struct {
Node* node;
int node_size;
} NodeList;

这个时候,不仅要给NodeList分配空间,也要给Node分配空间,即:

1
2
3
NodeList* nl = (NodeList*)malloc(sizeof(NodeList));
Node* nd = (Node*)malloc(sizeof(Node));
nl->node = nd;

否则就会造成段错误,比如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

typedef struct {
int a;
char d;
} Node;

typedef struct {
Node* nd;
int size;
} NodeList;

int main(int argc, char** argv) {
NodeList* nl = (NodeList*)malloc(sizeof(NodeList));
nl->size = 1;
nl->nd->a = 2; // ERROR
return 0;
}

因为,nl->nd并没有被开辟空间,访问这个地址是错误的。下面的代码就是正确的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

typedef struct {
int a;
char d;
} Node;

typedef struct {
Node* nd;
int size;
} NodeList;

int main(int argc, char** argv) {
NodeList* nl = (NodeList*)malloc(sizeof(NodeList));
nl->size = 1;
Node* nd = (Node*)malloc(sizeof(Node));
nl->nd = nd;
nl->nd->a = 2;
return 0;
}

malloc必须对应free

free()函数是用来销毁malloc()申请的空间的,二者是一个对应关系,哪里有malloc,在它后面就必须有free,否则会造成内存泄露。

比如下面的代码:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

int main(int argc, char** argv) {
int* a = (int*)malloc(sizeof(int));
return 0;
}

使用valgrind进行检查:

1
gcc test.c -o test; valgrind ./test

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
==30767== Memcheck, a memory error detector
==30767== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==30767== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
==30767== Command: ./a.out
==30767==
==30767==
==30767== HEAP SUMMARY:
==30767== in use at exit: 4 bytes in 1 blocks
==30767== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==30767==
==30767== LEAK SUMMARY:
==30767== definitely lost: 4 bytes in 1 blocks
==30767== indirectly lost: 0 bytes in 0 blocks
==30767== possibly lost: 0 bytes in 0 blocks
==30767== still reachable: 0 bytes in 0 blocks
==30767== suppressed: 0 bytes in 0 blocks
==30767== Rerun with --leak-check=full to see details of leaked memory
==30767==
==30767== For lists of detected and suppressed errors, rerun with: -s
==30767== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

这里的definitely lost的内存,就是我们malloc但是没有free掉的内存。我们如果将代码改为:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

int main(int argc, char** argv) {
int* a = (int*)malloc(sizeof(int));
free(a);
return 0;
}

再去检查,输出为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
==31045== Memcheck, a memory error detector
==31045== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==31045== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
==31045== Command: ./a.out
==31045==
==31045==
==31045== HEAP SUMMARY:
==31045== in use at exit: 0 bytes in 0 blocks
==31045== total heap usage: 1 allocs, 1 frees, 4 bytes allocated
==31045==
==31045== All heap blocks were freed -- no leaks are possible
==31045==
==31045== For lists of detected and suppressed errors, rerun with: -s
==31045== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

问题解决。

这表示,申请开辟的空间必须要去销毁。

更进一步,如果结构体出现了嵌套情况,则必须一层一层进行free。

文章作者:
文章链接: https://www.coderlock.site/2025/05/31/C语言的malloc分配注意事项/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 寒夜雨