【发布时间】:2017-07-04 02:01:01
【问题描述】:
我试图了解我的 C 编程知识发生了什么变化。让我们从一个 CLASSICAL 问题开始(取消引用未初始化的指针):
int main( void ) {
char *p;
*p = 'a';
printf( "%c\n", *p );
return 0;
}
这显然是错误的!我知道。但是,当我在我的 MacOS 10.12 上运行此程序并将 LLVM Clang 8.1.0 实现为编译器时,它不仅无法检测到未初始化的指针,而且还在屏幕上显示字符“a”,就好像没有任何问题一样。至少我期待像“Segmentation Fault”这样的东西。
请稍等一下,看下面的代码:
int main( void ) {
int i;
char **strPtr;
char *string = "Hello, world!";
*strPtr = string;
printf( "%s", *strPtr );
return 0;
}
这一次,我使用 GCC-4.8.5 运行代码,它已经成功检测到第一个代码中的分段错误。令我沮丧的是,字符串“Hello, world!”在我运行代码后出现在屏幕上,好像它没有任何问题。
我想知道,既然 strPtr 也是一个指针(尽管是指向指针的指针),通过取消引用并为 strPtr 赋值,我不应该犯下与第一个代码中相同的错误吗?
【问题讨论】:
-
告诉你的clang你希望它警告你这些东西(
-Wall),并且它应该考虑这些警告错误(-Werror)。换句话说,使用例如clang -Wall -Werror source.c -o binary. -
您的指针未初始化。任何神编译器,包括 clang 都会警告无意义的转换。如果您忽略它们(或不启用推荐的警告),这不是编译器的错
-
"但也显示了结果" - 哪个结果?该代码没有任何意义,但会调用未定义的行为。
-
当你编写一个调用未定义行为的程序时,any 行为对于 C 标准来说是“正确的”。不保证 Seg 故障。如果
p的垃圾内容碰巧是不在程序有效段内的地址,则会出现段错误。幸运的是,在您的情况下,p的垃圾内容恰好指向分配给您的程序的读/写内存。 -
诚然,编译器在未定义行为方面的差异解释了这个问题,但是您如何用第二段代码启发我?