具体来说,铸造malloc的结果有什么危险?
现在,在人们开始将这个标记为dup之前,我已经阅读了以下所有内容,其中没有一个提供了我正在寻找的答案:
C常见问题解答和上述问题的很多答案都引用了一个神秘的错误,即malloc
的返回值可以隐藏起来; 然而,他们没有一个在实践中给出这样一个错误的具体例子。 现在请注意,我说错误 ,而不是警告 。
现在给出以下代码:
#include <string.h>
#include <stdio.h>
// #include <stdlib.h>
int main(int argc, char** argv) {
char * p = /*(char*)*/malloc(10);
strcpy(p, "hello");
printf("%sn", p);
return 0;
}
使用gcc 4.2编译上述代码,使用和不使用强制转换会给出相同的警告,并且程序正确执行并在两种情况下都提供相同的结果。
anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc
hello
那么,任何人都可以给出一个特定的代码示例,说明由于投射malloc
的返回值而可能发生的编译或运行时错误,还是仅仅是城市传说?
编辑我遇到了两个关于这个问题的写得很好的论点:
你不会得到一个编译器错误 ,而是一个编译器警告 。 作为您引用的来源(特别是第一个),您可以在不使用stdlib.h
情况下使用转换时发生不可预知的运行时错误 。
所以你这边的错误不是演员,而是忘记包含stdlib.h
。 编译器可能会认为malloc
是一个返回int
的函数,因此由malloc
实际返回的void*
指针转换为int
,然后转换为指针类型,这是由于显式强制转换。 在某些平台上, int
和指针可能占用不同数量的字节,因此类型转换可能会导致数据损坏。
幸运的是,现代编译器给出了指向你的实际错误的警告。 请参阅您提供的gcc
输出:它会警告您隐式声明( int malloc(int)
)与内置malloc
不兼容。 所以即使没有stdlib.h
gcc
似乎也知道malloc
。
抛出演员以防止这种错误与写作大体上是相同的推理
if (0 == my_var)
代替
if (my_var == 0)
因为后者可能会导致严重的错误,如果你会混淆=
和==
,而第一个会导致编译错误。 我个人更喜欢后一种风格,因为它更好地反映了我的意图,我不倾向于犯这个错误。
对于转换由malloc
返回的值也是如此:我更喜欢在编程中明确表示,我通常仔细检查包含所有我使用函数的头文件。
对于malloc
的结果,其中一个较好的更高级别的观点经常被忽略,尽管在我看来,它比知名的底层问题更重要(比如声明丢失时截断指针) 。
一个好的编程习惯是编写代码,它尽可能的与类型无关。 这意味着,尤其应该在代码中尽可能少地提及类型名称,或者最好不要提及类型名称。 这适用于强制类型转换(避免不必要的强制类型转换),作为sizeof
参数的sizeof
(避免在sizeof
使用类型名称)以及通常所有其他对类型名称的引用。
类型名称属于声明。 尽可能多的,类型名称应该限制在声明中,并且仅限于声明。
从这个角度来看,这一点代码是不好的
int *p;
...
p = (int*) malloc(n * sizeof(int));
这样好多了
int *p;
...
p = malloc(n * sizeof *p);
不只是因为它“不投结果malloc
”,而是因为它的类型是独立的(或类型agnositic,如果你喜欢),因为它会自动调整自己到任何类型的p
声明了,而不需要任何来自用户的干预。
假定非原型函数返回int
。
所以你正在将一个int
投射到一个指针上。 如果指针在你的平台上比int
宽,这是非常危险的行为。
此外,当然,有些人认为警告是错误的,即代码应该在没有它们的情况下编译。
就我个人而言,我认为你不需要将void *
为另一种指针类型是C中的一个特性,并且考虑那些被破坏的代码。
上一篇: Specifically, what's dangerous about casting the result of malloc?
下一篇: Getting a stack overflow exception when declaring a large array