"Pointer to pointer to int issue"
"Pointer to pointer to int issue"
今天我试图解决 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 );
}
问题是:
Choose the correct statement w.r.t. above C program:
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)
不正确。
但是因为这里没有 compile/run 时间错误,使得答案 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
上测试
答案C最接近正确。行
free( *ppInt2 );
绝对不正确。该错误不是编译器可以检测到的错误。但它很可能会导致 运行-time 错误。 (但不保证会导致 运行 次错误。更多内容请见下文。)
malloc
和 free
的规则非常简单:您传递给 free
的每个指针必须与您从上一次调用 malloc
收到的指针完全相同(或 calloc
,或 realloc
)。在代码中,malloc
和 free
调用 pInt
和 ppInt1
正确遵循此规则。但是对于ppInt2
来说,malloc
返回的指针赋值给了ppInt2
,但是交给free
的指针是*ppInt2
,[=指向的值22=]。但是由于 *ppInt2
—— 即 ppInt2
指向的值 —— 没有以任何方式初始化,它是一个垃圾值, free
可能会崩溃。最终结果或多或少就像你说的那样
int main()
{
int *p;
free(p); /* WRONG */
}
但是,同样,不能保证一定会崩溃。因此,更正确的答案将被表述为
C' - free(*ppInt2)
is not correct. It’ll likely give a run time error.
恐怕说答案 D 正确的人可能并不真正知道他们在说什么。我建议不要继续进行此测验 -- 谁知道它包含多少其他错误或误导性答案?
总是很难理解未定义的行为,因为未定义的行为意味着任何事情都可能发生,包括什么都没有。当有人说 "I heard that doing X was undefined, but I tried it, and it worked fine" 时,就像说 "I heard that running across a busy street was dangerous, but I tried it, and it worked fine."
关于未定义行为的另一件事是你必须仔细考虑并理解它。几乎根据定义,没有语言翻译工具——没有 C 编译器或其他工具——保证会警告您。 您 必须知道什么是未定义的,因此要避免什么。你 不能 说 "Well, my program compiles without errors or warnings, and it seems to work, so it must be correct." 换句话说,你不能试图将 "correct vs. incorrect" 的决定强加到机器上——你必须拥有这种区别.
但也许你知道这一切。也许真正的问题很简单,"If answer C is correct, how can the program not fail with a run-time error, in fact how can it repeatedly not fail?"这个问题有两个答案:
如前所述,未定义的行为意味着 任何事情 都可能发生,包括什么都没有(即没有错误),包括在多个连续的 运行 上什么也没有。
在许多系统上,第一次 malloc
给你一个指向一些全新内存的指针,它总是全位 0(也就是说,或多或少就像你'称为 calloc
)。这绝对是
不 由 C 标准保证——你应该从不依赖它——但在那些系统上,它很可能也可能是保证。此外,在几乎所有系统上,全位为 0 的指针值都是空指针。因此,再次仅在那些特定系统上,并且仅在第一次 malloc
为您提供指向全新内存的指针时,代码 ppInt2 = malloc(10 * sizeof(int*))
将为您提供 10 个空指针。由于 free
被定义为如果您向它传递空指针则什么也不做,在这种特定情况下,free(*ppInt2)
永远不会失败,即使在 运行 时也不会。 (也许这就是设置测验的人的想法,因为 如果 你做了这些额外的假设,那么写的答案 C 基本上是不正确的,而答案 D 基本上是——我讨厌承认这一点——或多或少是准确的。)
回到前面的类比,如果有人做出这些额外的假设,并注意到代码永远不会失败,并试图声称答案 D 是正确的,这基本上就像在说 "I heard that running across the street was dangerous, but I tried it in the middle of the night, and it worked fine. I even ran back and forth ten times. I never got hit by a car, not even once"。而且,不幸的是,那里有一些遵循类似逻辑的程序员,他们将编写程序来执行 C 编程等价于街对面的 运行ning。然后这些程序员抱怨,好像这不是他们的错,当他们的运气不可避免地 运行 结束并且出现可怕的致命崩溃时。
我们来解决这个问题:
- 这里没有编译时错误
- 这里也没有(ISO C 标准指定的)运行时错误,因为在 C 中只有 none 个运行时错误。本质上,唯一的运行时错误是标准库函数的错误(返回) .
free(*ppInt2)
是未定义的行为。任何事情都可能发生。编译器可能会删除它,甚至可能会删除整个 main()
,或者更糟。如果它只是保持原样,free()
函数本身也可以做任何事情——忽略、崩溃、报告错误、通过尝试释放给定指针来搞乱它的簿记...
- 这是一个编码错误。不幸的是,像 C 中的许多语言一样,它没有被语言编译器或它的 runtime/standard 库捕获。
Valgrind 捕捉到这一点的事实是该工具的一个很好的卖点,但是,它不是 C 语言的一部分。
今天我试图解决 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 );
}
问题是:
Choose the correct statement w.r.t. above C program:
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)
不正确。
但是因为这里没有 compile/run 时间错误,使得答案 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
答案C最接近正确。行
free( *ppInt2 );
绝对不正确。该错误不是编译器可以检测到的错误。但它很可能会导致 运行-time 错误。 (但不保证会导致 运行 次错误。更多内容请见下文。)
malloc
和 free
的规则非常简单:您传递给 free
的每个指针必须与您从上一次调用 malloc
收到的指针完全相同(或 calloc
,或 realloc
)。在代码中,malloc
和 free
调用 pInt
和 ppInt1
正确遵循此规则。但是对于ppInt2
来说,malloc
返回的指针赋值给了ppInt2
,但是交给free
的指针是*ppInt2
,[=指向的值22=]。但是由于 *ppInt2
—— 即 ppInt2
指向的值 —— 没有以任何方式初始化,它是一个垃圾值, free
可能会崩溃。最终结果或多或少就像你说的那样
int main()
{
int *p;
free(p); /* WRONG */
}
但是,同样,不能保证一定会崩溃。因此,更正确的答案将被表述为
C' -
free(*ppInt2)
is not correct. It’ll likely give a run time error.
恐怕说答案 D 正确的人可能并不真正知道他们在说什么。我建议不要继续进行此测验 -- 谁知道它包含多少其他错误或误导性答案?
总是很难理解未定义的行为,因为未定义的行为意味着任何事情都可能发生,包括什么都没有。当有人说 "I heard that doing X was undefined, but I tried it, and it worked fine" 时,就像说 "I heard that running across a busy street was dangerous, but I tried it, and it worked fine."
关于未定义行为的另一件事是你必须仔细考虑并理解它。几乎根据定义,没有语言翻译工具——没有 C 编译器或其他工具——保证会警告您。 您 必须知道什么是未定义的,因此要避免什么。你 不能 说 "Well, my program compiles without errors or warnings, and it seems to work, so it must be correct." 换句话说,你不能试图将 "correct vs. incorrect" 的决定强加到机器上——你必须拥有这种区别.
但也许你知道这一切。也许真正的问题很简单,"If answer C is correct, how can the program not fail with a run-time error, in fact how can it repeatedly not fail?"这个问题有两个答案:
如前所述,未定义的行为意味着 任何事情 都可能发生,包括什么都没有(即没有错误),包括在多个连续的 运行 上什么也没有。
在许多系统上,第一次
malloc
给你一个指向一些全新内存的指针,它总是全位 0(也就是说,或多或少就像你'称为calloc
)。这绝对是 不 由 C 标准保证——你应该从不依赖它——但在那些系统上,它很可能也可能是保证。此外,在几乎所有系统上,全位为 0 的指针值都是空指针。因此,再次仅在那些特定系统上,并且仅在第一次malloc
为您提供指向全新内存的指针时,代码ppInt2 = malloc(10 * sizeof(int*))
将为您提供 10 个空指针。由于free
被定义为如果您向它传递空指针则什么也不做,在这种特定情况下,free(*ppInt2)
永远不会失败,即使在 运行 时也不会。 (也许这就是设置测验的人的想法,因为 如果 你做了这些额外的假设,那么写的答案 C 基本上是不正确的,而答案 D 基本上是——我讨厌承认这一点——或多或少是准确的。)
回到前面的类比,如果有人做出这些额外的假设,并注意到代码永远不会失败,并试图声称答案 D 是正确的,这基本上就像在说 "I heard that running across the street was dangerous, but I tried it in the middle of the night, and it worked fine. I even ran back and forth ten times. I never got hit by a car, not even once"。而且,不幸的是,那里有一些遵循类似逻辑的程序员,他们将编写程序来执行 C 编程等价于街对面的 运行ning。然后这些程序员抱怨,好像这不是他们的错,当他们的运气不可避免地 运行 结束并且出现可怕的致命崩溃时。
我们来解决这个问题:
- 这里没有编译时错误
- 这里也没有(ISO C 标准指定的)运行时错误,因为在 C 中只有 none 个运行时错误。本质上,唯一的运行时错误是标准库函数的错误(返回) .
free(*ppInt2)
是未定义的行为。任何事情都可能发生。编译器可能会删除它,甚至可能会删除整个main()
,或者更糟。如果它只是保持原样,free()
函数本身也可以做任何事情——忽略、崩溃、报告错误、通过尝试释放给定指针来搞乱它的簿记...- 这是一个编码错误。不幸的是,像 C 中的许多语言一样,它没有被语言编译器或它的 runtime/standard 库捕获。
Valgrind 捕捉到这一点的事实是该工具的一个很好的卖点,但是,它不是 C 语言的一部分。