问题描述
在最近的考试问题中,我获得了带有以下选项的代码:
char **mptr,*pt1; int i; mptr = calloc(10,sizeof(char*)); for (i=0; i<10; i++) { mptr[i] = ( char *)malloc(10); }
以下哪些解除分配策略会导致内存泄漏?
A。
free(mptr);
B。
for(i = 0; i < 10; i++): { free(mptr[i]); }
C。全部
答案是C。但是在我看来,应用free(mptr);
可以解决内存泄漏,B也是如此,尽管我不太确定,但是有人可以解释一下为什么所有人都会这样做吗?导致内存泄漏?
我猜想C选项期望每个操作(A或B)是分开应用的。
P.S。
我看不到这段代码的重点,如果您已经用calloc
分配了内存(并对其进行了初始化),为什么还要花一个循环分配每个单元呢?我不相信吗?
解决方法
以下哪些解除分配策略会导致内存泄漏?
在我的专家看来,正确的答案必须是选项A
,因为它会释放mptr
,导致mptr[i]
指针不可访问,因此会导致内存泄漏。假定其他方式完全无法访问内存,则以后无法释放它们。
选项B本身不会导致内存泄漏 ,在释放mptr
指针之后,mptr[i]
仍然可以访问。您可以重用它,也可以稍后再分配它。只有当您失去对mptr
所指向的内存的访问权限时,才会发生内存泄漏。
我认为这个问题的格式有些错误,如果问题是“您将使用哪个选项来正确地释放所有内存?” ,那么,选项C
是正确。
我确实同意取消所有内存的正确策略是B
+ A
,尽管首先是A
会导致立即的内存泄漏,而{{ 1}}首先将允许以后B
的重新分配,只要对它所指向的内存的访问没有丢失。
我看不到这段代码的重点,如果您已经用calloc分配了内存(并对其进行了初始化),为什么还要花一个循环分配每个单元呢?我不相信吗?
分配正确。
mptr
Check this thread了解更多信息。
,让我们把它画出来
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[i]
之前先分配每个ptr
); -
free
必须的参数必须是从malloc
,calloc
或realloc
调用返回的指针。
P.S。我看不到这段代码的重点,如果您已经用
calloc
分配了内存(并对其进行了初始化),为什么还要花一个循环分配每个单元呢?我不相信吗?
这是一个“锯齿状”数组的示例,其中每个“行”的长度可以不同(常规2D数组无法做到)。如果要存储(例如)所有不同长度的单词列表,这会很方便:
char ** char * char
+---+ +---+ +---+---+---+---+
| | -------->| |-------->|'f'|'o'|'o'| 0 |
+---+ +---+ +---+---+---+---+
| | -----+
+---+ | +---+---+---+---+---+---+---+
| | ---+ +->|'b'|'l'|'u'|'r'|'g'|'a'| 0 |
+---+ | +---+---+---+---+---+---+---+
... |
| +---+---+---+---+---+---+
+--->|'h'|'e'|'l'|'l'|'o'| 0 |
+---+---+---+---+---+---+
如有必要,您可以轻松调整每个“行”的大小而不会影响其他任何行,并且可以轻松添加更多“行”。当您为 索引时,它看起来像是2D数组-您可以像其他任何2D数组一样使用mptr[i][j]
访问单个元素-但“行”在内存中并不连续。
将此与“真实的” 2D数组进行比较,其中所有行的大小均相同并且连续布置:
+---+---+---+---+---+---+---+
|'f'|'o'|'o'| 0 | ? | ? | ? |
+---+---+---+---+---+---+---+
|'b'|'l'|'u'|'r'|'g'|'a'| 0 |
+---+---+---+---+---+---+---+
|'h'|'e'|'l'|'l'|'o'| 0 | ? |
+---+---+---+---+---+---+---+
主要缺点是浪费了一些空间。必须将数组的大小调整为要存储的最长单词。如果您有一个包含100个字符串的表,其中一个字符串的长度为100个字符,其余的为10个字符,那么您将浪费很多空间。您不能有比其他行更长的行。
优点是行是连续的,因此沿着数组“遍历”更加容易。
请注意,您也可以动态分配常规2D数组:
char (*ptr)[10] = calloc( 10,sizeof *ptr );
这会为char
的10x10阵列分配足够的空间 ,您可以像其他任何2D数组一样将其编入索引:
strcpy( ptr[0],"foo" );
ptr[0][0] = 'F';
由于这是一次分配,因此您只需要一次取消分配:
free( ptr );
,
free(mptr);
仅释放分配给指针的内存,而不释放指针指向的内存。
如果在释放指针所指向的内存之前free()
为指针分配了内存,那么您将不再有指向要指向的内存的引用,因此会发生内存泄漏。
另一方面,
for(i = 0; i < 10; i++): { free(mptr[i]); }
仅释放指向的内存,而没有释放指针。根据您对它的严格程度的考虑,您也可以将其视为内存泄漏,因为指针的内存未释放。
因此,根据观点和个人观点,A。或C.是正确的。
我看不到这段代码的重点,如果您已经用
calloc
分配了内存(并对其进行了初始化),为什么还要为每个单元分配一个循环呢?
使用
mptr = calloc(10,sizeof(char*));
您为指针mptr
所指向的 pointers 本身分配了内存。
但是指针需要指向您可以使用指针访问的数据存储器。指针本身不能存储指向指向的内存地址以外的任何其他数据。
因此,您需要通过for
循环内的每次迭代为每个指针分配指向的内存。
指针始终需要指向一个位置才能正确使用它作为指针(异常:空指针)。