引子
我一直以来都很喜欢C语言——纯粹的C语言,不加任何C++。这是一种纯粹的开发过程,我是内存的巫师。
我在YouTube关注了一个开发者,叫做Tsoding。他用C语言实现了很多有意思的东西——音乐可视化器、BPE算法等等。
然而,许久不用,我已经快忘记了如何进行基本的内存管理,这篇文章就来记叙一下我写Side Project过程中遇到的一些问题。
这次我的Side Project是动态表(Dynamic Table, Dynamic Array),这是《算法导论》一书中所遇到的第一个数据结构(或许)。
嵌套结构体的malloc
嵌套结构体需要一层一层分配空间,比如:
typedef struct {
int actual_value;
char description;
} Node;
typedef struct {
Node* node;
int node_size;
} NodeList;这个时候,不仅要给NodeList分配空间,也要给Node分配空间,即:
NodeList* nl = (NodeList*)malloc(sizeof(NodeList));
Node* nd = (Node*)malloc(sizeof(Node));
nl->node = nd;否则就会造成段错误,比如下面的代码:
#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并没有被开辟空间,访问这个地址是错误的。下面的代码就是正确的:
#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,否则会造成内存泄露。
比如下面的代码:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main(int argc, char** argv) {
int* a = (int*)malloc(sizeof(int));
return 0;
}使用valgrind进行检查:
gcc test.c -o test; valgrind ./test输出如下:
==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掉的内存。我们如果将代码改为:
#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;
}再去检查,输出为:
==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。