今天,我尝试解决Here中的一个测验,当我到达问题3时,有以下代码:
#include <stdlib.h>
int main(void){
int *pInt;
int **ppInt1;
int **ppInt2;
pInt = (int*)malloc(sizeof(int));
ppInt1 = (int**)malloc(10*sizeof(int*));
ppInt2 = (int**)malloc(10*sizeof(int*));
free( pInt );
free( ppInt1 );
free( *ppInt2 );
}
问题是:
选择正确的陈述以上C程序:
A - malloc() for ppInt1 and ppInt2 isn’t correct. It’ll give compile time error.
B - free(*ppInt2) is not correct. It’ll give compile time error.
C - free(*ppInt2) is not correct. It’ll give run time error.
D - No issue with any of the malloc() and free() i.e. no compile/run time error
因为这一行:
free(*ppInt2);
据我了解,这表明不会出现编译或运行时错误,我决定
free(*ppInt2)
不正确。
但是因为这里没有编译/运行时错误,所以答案B
和C
都是错误的。
作者说接受的答案是:
D - No issue with any of the malloc() and free() i.e. no compile/run time error.
现在这是我的问题,为什么没有问题,因为这样做:
free( *ppInt2 );
Valgrind报告:
==9468== Memcheck, a memory error detector
==9468== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9468== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==9468== Command: ./program
==9468==
==9468== Conditional jump or move depends on uninitialised value(s)
==9468== at 0x4C30CF1: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9468== by 0x1086C1: main (program.c:14)
==9468== Uninitialised value was created by a heap allocation
==9468== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9468== by 0x108696: main (program.c:10)
==9468==
==9468==
==9468== HEAP SUMMARY:
==9468== in use at exit: 80 bytes in 1 blocks
==9468== total heap usage: 3 allocs, 2 frees, 164 bytes allocated
==9468==
==9468== 80 bytes in 1 blocks are definitely lost in loss record 1 of 1
==9468== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9468== by 0x108696: main (program.c:10)
==9468==
==9468== LEAK SUMMARY:
==9468== definitely lost: 80 bytes in 1 blocks
==9468== indirectly lost: 0 bytes in 0 blocks
==9468== possibly lost: 0 bytes in 0 blocks
==9468== still reachable: 0 bytes in 0 blocks
==9468== suppressed: 0 bytes in 0 blocks
==9468==
==9468== For counts of detected and suppressed errors, rerun with: -v
==9468== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
我认为正确的free
通话应该是:
free( ppInt2 );
在Linux mint 19
,GCC-8
和valgrind-3.13.0
上进行了测试
答案 0 :(得分:7)
答案C最接近正确的答案。线
free( *ppInt2 );
绝对不正确。该错误不是编译器可以检测到的错误。但这很可能导致运行时错误。 (但不能保证会导致运行时错误。下面更多内容。)
malloc
和free
的规则非常简单:您传递给free
的每个指针必须与您先前对malloc
的调用所收到的指针完全相同(或calloc
或realloc
)。在代码中,malloc
和free
的调用pInt
和ppInt1
正确地遵循了此规则。但是对于ppInt2
,将malloc
返回的指针分配给ppInt2
,但是传递给free
的指针是*ppInt2
,即{{ 1}}。但是由于ppInt2
(即*ppInt2
指向的值)尚未进行任何初始化,因此它是一个垃圾值,ppInt2
可能会崩溃。最终结果或多或少与您说的完全一样
free
但是,同样不能保证崩溃。因此,更正确的答案将写为
C'-
int main() { int *p; free(p); /* WRONG */ }
不正确。可能会给出运行时错误。
恐怕有人说答案D是正确的,可能真的不知道他们在说什么。我建议不要继续进行此测验-谁知道该测验还包含其他错误或误导性答案?
很难理解未定义的行为,因为未定义的行为意味着任何事情都可能发生,包括什么也没有发生。当有人说“我听说做 X 并没有定义,但是我尝试了,而且效果很好”时,就像在说“我听说在繁忙的街道上奔跑是危险的,但是我尝试了,效果很好。“
关于未定义行为的另一件事是,您必须仔细考虑并理解它。从定义上讲,几乎没有语言翻译工具(没有C编译器或其他工具)可以保证就此警告您。 您必须知道什么是未定义的,因此要避免使用什么。您不能说:“好,我的程序编译时没有错误或警告,并且似乎可以正常运行,因此它必须正确。”换句话说,您不能试图将“正确与不正确”的判断强加给机器,而必须拥有这种区别。
但是也许您知道所有这些。也许真正的问题很简单,即“如果答案C是正确的,那么程序 not 怎么会由于运行时错误而失败,而实际上又怎么会反复失败呢?”这个问题有两个答案:
如前所述,未定义的行为意味着任何事情都可能发生,包括什么都没有(即没有错误),包括连续多次运行都没有的事情。
在许多系统上,free(*ppInt2)
第一次为您提供一些崭新的内存的指针时,它始终是全零(即,或多或少地就像您被称为{ {1}})。这绝对是
C标准不能保证 –您永远不要 依赖它-但是在那些系统上,很有可能也可以保证它。此外,在几乎所有系统上,全为0的指针值是空指针。因此,仅在那些特定系统上,并且仅在malloc
第一次给您提供指向全新内存的指针时,代码calloc
将为您提供10个空指针。并且由于malloc
被定义为如果您传递空指针,则不执行任何操作,因此在这种特定情况下,ppInt2 = malloc(10 * sizeof(int*))
将永远不会失败,甚至在运行时也不会失败。 (也许这就是进行测验的人所想到的,因为 if 您做出了这些额外的假设,答案C基本上是不正确的,答案D基本上是-我讨厌承认这一点-或多或少准确。)
回到较早的类比,如果有人做出这些附加假设,并且注意到代码从未失败,并试图声称答案D是正确的,那基本上就像在说:“我听说过马路很危险。 ,但我在半夜尝试了一下,效果还不错。我什至来回跑了十次。我从来没有碰过汽车,甚至一次都没有。”而且,不幸的是,那里的程序员遵循类似的逻辑,他们将编写与C语言编程等效的程序,这些代码始终在街上运行。然后,这些程序员抱怨说,这不是他们的错,这是因为他们的运气不可避免地耗尽,并且发生了致命的致命崩溃。
答案 1 :(得分:1)
让我们来解决这个问题:
free(*ppInt2)
是未定义的行为。什么都可能发生。编译器可能会删除它,甚至可能删除整个main()
,甚至更糟。如果只是按原样保留,free()
函数本身也可能会做任何事情-忽略,崩溃,报告错误,通过尝试释放给定的指针来弄乱其簿记... Valgrind抓住了这一事实,这对该工具来说是一个不错的卖点,但它不是C语言的一部分。