具体来说,铸造malloc的结果有什么危险?
现在,在人们开始标记这个dup之前,我已经阅读了以下所有内容,但是没有一个提供我正在寻找的答案:
- C FAQ:铸造malloc的返回值有什么问题?
- SO:我应该明确地施放malloc()的返回值吗?
- SO:C中不需要的指针
- SO:我inputmalloc的结果吗?
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("%s\n", 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
的返回值而产生的,或者这只是一个城市传说?
编辑我已经遇到了两个关于这个问题写得很好的论据:
- 有利于 Casting:CERT咨询: 立即将内存分配函数调用的结果转换为指向分配types的指针
- 针对Casting (2012年2月14日发生404错误:使用Internet Archive Wayback Machine复制自2010-01-27。{2016-03-18:“由于robots.txt导致页面无法被抓取或显示”)}
你不会得到一个编译器错误 ,而是一个编译器警告 。 作为引用的来源(特别是第一个 ),当使用转换时, 如果不包含stdlib.h
, 可能会导致无法预料的运行时错误 。
所以错误在你身边不是演员,但忘记包含stdlib.h
。 编译器可能会认为malloc
是一个返回int
的函数,因此由malloc
实际返回的void*
指针转换为int
,然后转换为指针types。 在某些平台上, int
和指针可能会占用不同数量的字节,因此types转换可能会导致数据损坏。
幸运的是,现代编译器给出了指向你的实际错误的警告。 请参阅您提供的gcc
输出:它会警告您隐式声明( int malloc(int)
)与内置malloc
不兼容。 所以即使没有stdlib.h
gcc
似乎也知道malloc
。
抛出演员,以防止这个错误大多是写作相同的原因
if (0 == my_var)
代替
if (my_var == 0)
因为后者可能会导致严重的错误,如果有人会混淆=
和==
,而第一个会导致编译错误。 我个人比较喜欢后者,因为它更好地反映了我的意图,我不倾向于犯这个错误。
对于转换由malloc
返回的值也是如此:我更喜欢在编程中明确表示,我通常仔细检查包含所有我使用的函数的头文件。
其中一个反对malloc
结果的较高层次的争论经常被忽略,尽pipe在我看来,它比着名的底层问题更重要(比如声明丢失时截断指针) 。
一个好的编程习惯是编写代码,尽可能的不依赖于types。 这意味着,特别是在代码中应尽可能less地提及types名称,或者最好不要提及types名称。 这适用于强制types转换(避免不必要的强制types转换),作为sizeof
参数的sizeof
(避免使用sizeof
types名称),通常还包括对types名称的所有其他引用。
types名称属于声明。 尽可能地,types名称应该只限于声明,并且只限于声明。
从这个angular度来看,这一点代码是不好的
int *p; ... p = (int*) malloc(n * sizeof(int));
这样好多了
int *p; ... p = malloc(n * sizeof *p);
不是简单的因为它“不会传递malloc
的结果”,而是因为它是types无关的(或者types无关的,如果你喜欢的话),因为它自动调整自己到任何types的p
声明,而不需要任何来自用户的干预。
假定非原型函数返回int
。
所以你正在投射一个int
指针。 如果指针比你的平台上的int
更宽,这是非常危险的行为。
此外,当然,有些人认为警告是错误的,即代码应该编译没有他们。
就个人而言,我认为你不需要将void *
为另一个指针types是C中的一个特性,并且考虑到了可以被破坏的代码。
如果以64位模式编译时执行此操作,则返回的指针将被截断为32位。
编辑:抱歉太短暂。 这里是一个用于讨论目的的示例代码片段。
主要() { char * c =(char *)malloc(2); printf(“%p”,c); }
假设返回的堆指针比int中可表示的东西大,比如0xAB00000000。
如果malloc没有原型返回一个指针,那么返回的int值最初将会被寄存器中的所有有效位设置。 现在编译器说,“好吧,我该如何转换和int指针”。 这将是低位32位的符号扩展或零扩展,它通过省略原型而被告知malloc“返回”。 由于int是有符号的,我认为转换将是符号扩展,在这种情况下,将值转换为零。 返回值为0xABF0000000时,您将得到一个非零指针,当您尝试对其进行解引用时,也会带来一些乐趣。
可重复使用的软件规则:
在编写使用malloc()的内联函数的情况下,为了使C ++代码也可以重用,请做一个明确的types转换(例如(char *)); 否则编译器会报错。
C中的void指针可以分配给任何指针而不需要显式的强制转换。 编译器会给出警告,但是可以通过将malloc()
强制转换为相应的types来在C ++中重用 。 用outtypes转换也可以在C中使用,因为C没有严格的types检查 。 但是C ++是严格types检查的,因此需要在C ++中inputcast malloc()
。