让我们把它画出来:
char ** char * char
+---+ +---+ +---+---+ +---+
mptr: | | -------->| | mptr[0] -------->| | | ... | |
+---+ +---+ +---+---+ +---+
| | mptr[1] ------+
+---+ | +---+---+ +---+
... +->| | | ... | |
+---+ +---+---+ +---+
| | mptr[9] ----+
+---+ | +---+---+ +---+
+--->| | | ... | |
+---+---+ +---+
如果你所做的只是释放mptr 指向的内存,你会得到这样的结果:
char ** char
+---+ +---+---+ +---+
mptr: | | | | | ... | |
+---+ +---+---+ +---+
+---+---+ +---+
| | | ... | |
+---+---+ +---+
+---+---+ +---+
| | | ... | |
+---+---+ +---+
每个mptr[i] 的分配没有被释放。这些都是单独的分配,并且必须在您释放mptr 之前单独释放。 free 不会检查它正在释放的内存的内容,以确定是否有任何嵌套分配也需要释放。正确的程序是写
for ( int i = 0; i < 10; i++ )
free( mptr[i] );
free( mptr );
如果你所做的只是释放每个 mptr[i] 而不是 mptr,你会得到这个:
char ** char *
+---+ +---+
mptr: | | -------->| | mptr[0]
+---+ +---+
| | mptr[1]
+---+
...
+---+
| | mptr[9]
+---+
您仍然拥有最初分配的指针数组。现在,这还不是内存泄漏 - 只有当您忘记了 mptr 时,它才会变成一个。
所以,这些是 C 中的内存管理规则:
- 每个
malloc、calloc 或realloc 调用最终都必须有一个对应的free;
- 在进行嵌套分配时,始终按照您分配的相反顺序解除分配(即,在解除分配
ptr 之前解除分配每个 ptr[i]);
-
free 的参数必须是从malloc、calloc 或realloc 调用返回的指针。
附:我看不出这段代码的意义,如果您已经使用calloc 分配了内存(并对其进行了初始化),为什么还要为每个单元分配一个循环?我有错吗?
这是一个“锯齿状”数组的示例,其中每个“行”可以是不同的长度(这是常规二维数组无法做到的)。如果您要存储(例如)所有不同长度的单词列表,这会很方便:
char ** char * char
+---+ +---+ +---+---+---+---+
| | -------->| |-------->|'f'|'o'|'o'| 0 |
+---+ +---+ +---+---+---+---+
| | -----+
+---+ | +---+---+---+---+---+---+---+
| | ---+ +->|'b'|'l'|'u'|'r'|'g'|'a'| 0 |
+---+ | +---+---+---+---+---+---+---+
... |
| +---+---+---+---+---+---+
+--->|'h'|'e'|'l'|'l'|'o'| 0 |
+---+---+---+---+---+---+
如有必要,您可以轻松调整每个“行”的大小而不会影响其他任何“行”,并且您可以轻松添加更多“行”。这个看起来当你索引它时就像一个二维数组 - 你可以像任何其他二维数组一样使用mptr[i][j] 访问单个元素 - 但“行”在内存中并不连续。
将此与“真实”二维数组进行比较,其中所有行的大小相同且连续布置:
+---+---+---+---+---+---+---+
|'f'|'o'|'o'| 0 | ? | ? | ? |
+---+---+---+---+---+---+---+
|'b'|'l'|'u'|'r'|'g'|'a'| 0 |
+---+---+---+---+---+---+---+
|'h'|'e'|'l'|'l'|'o'| 0 | ? |
+---+---+---+---+---+---+---+
主要缺点是一些浪费的空间。您的数组的大小必须适合您要存储的最长单词。如果您有一个包含 100 个字符串的表,其中一个长度为 100 个字符,其余 10 个字符长,那么您将浪费大量空间。一行不能比其他行长。
优点是行是连续的,因此更容易“遍历”数组。
请注意,您也可以动态分配常规二维数组:
char (*ptr)[10] = calloc( 10, sizeof *ptr );
这为 char 的 10x10 数组分配了足够的空间在单个分配中,您可以像任何其他二维数组一样对其进行索引:
strcpy( ptr[0], "foo" );
ptr[0][0] = 'F';
由于这是一次分配,因此您只需要一次解除分配:
free( ptr );