具体来说,铸造malloc的结果有什么危险?

时间:2009-10-14 10:34:14

标签: c

现在在人们开始将这个标记为复制之前,我已经阅读了以下所有内容,其中没有一个提供我正在寻找的答案:

  1. C FAQ: What's wrong with casting malloc's return value?
  2. SO: Should I explicitly cast malloc()’s return value?
  3. SO: Needless pointer-casts in C
  4. SO: Do I cast the result of malloc?
  5. C FAQ和上述问题的许多答案都引用了一个神秘的错误,即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的返回值而可能发生的编译或运行时错误,还是仅仅是一个城市传奇?

    编辑我在这个问题上遇到了两个写得很好的论点:

    1. 赞成投射:CERT顾问:Immediately cast the result of a memory allocation function call into a pointer to the allocated type
    2. Against Casting(截至2012-02-14的404错误:使用2010-01-27中的Internet Archive Wayback Machine副本。{2016-03-18:“由于以下内容无法抓取或显示页面的robots.txt。“})

6 个答案:

答案 0 :(得分:65)

您不会遇到编译器错误,而是编译器警告。作为您引用的来源(特别是first one),当使用强制转换而不包括{{1}时,您可以获得不可预测的运行时错误 }

所以你身边的错误不是演员,而是忘记包括stdlib.h。编译器可能会认为stdlib.h是一个返回malloc的函数,因此将int实际返回的void*指针转换为malloc,然后将指针类型转换为指针类型显式演员。在某些平台上,int和指针可能会占用不同的字节数,因此类型转换可能会导致数据损坏。

幸运的是,现代编译器会发出警告,指出您的实际错误。请参阅您提供的int输出:它警告您隐式声明(gcc)与内置int malloc(int)不兼容。因此即使没有mallocgcc似乎也知道malloc

离开演员阵容以防止出现此错误与编写

的理由大致相同
stdlib.h

而不是

if (0 == my_var)

因为后者可能会导致严重的错误,如果有人会混淆if (my_var == 0) =,而第一个会导致编译错误。我个人更喜欢后一种风格,因为它更能反映我的意图,而且我不倾向于犯这个错误。

对于转换==返回的值也是如此:我更喜欢在编程中明确,我通常会仔细检查以包含我使用的所有函数的头文件。

答案 1 :(得分:44)

反对投射malloc结果的一个好的高级别论点往往没有被提及,尽管在我看来,它比着名的低层问题更重要(比如截断声明丢失时的指针。)

良好的编程习惯是编写代码,尽可能与类型无关。这尤其意味着,应尽可能少地在代码中提及类型名称,或者最好不要提及。这适用于强制转换(避免不必要的强制转换),类型作为sizeof的参数(避免在sizeof中使用类型名称),以及通常所有其他对类型名称的引用。

类型名称属于声明。应尽可能将类型名称限制为声明,并仅限于声明。

从这个角度来看,这段代码很糟糕

int *p;
...
p = (int*) malloc(n * sizeof(int));

这是更好的

int *p;
...
p = malloc(n * sizeof *p);

不仅仅是因为它“不会转换malloc”的结果,而是因为它与类型无关(或者如果你愿意的话,还是类型agnositic),因为它会自动将自身调整为任何类型{声明{1}},无需用户进行任何干预。

答案 2 :(得分:18)

假设非原型函数返回int

所以你要向指针投射int。如果指针比平台上的int更宽,则这是一种高风险的行为。

另外,当然,有些人会考虑警告错误,即代码应该在没有它们的情况下进行编译。

就我个人而言,我认为您不需要将void *强制转换为另一种指针类型这一事实是C中的一项功能,并考虑要破解的代码。

答案 3 :(得分:11)

如果在64位模式下编译时执行此操作,则返回的指针将被截断为32位。

编辑: 抱歉太简短了。以下是用于讨论目的的示例代码片段。

main()
{
   char * c = (char *)malloc(2) ;
   printf("%p", c) ;
}

假设返回的堆指针大于int中可表示的值,比如说0xAB00000000。

如果malloc没有原型返回指针,则返回的int值最初将位于某个寄存器中,并设置了所有有效位。现在编译器说,“好吧,我如何将int和int转换为指针”。这将是一个符号扩展或低阶32位的零扩展,它已被告知malloc通过省略原型“返回”。由于int已签名,我认为转换将是符号扩展,在这种情况下将转换为零。返回值为0xABF0000000时,您将获得一个非零指针,当您尝试取消引用它时,这也会带来一些乐趣。

答案 4 :(得分:4)

可重复使用的软件规则:

在编写使用malloc()的内联函数的情况下,为了使其可以重用C ++代码,请进行显式类型转换(例如(char *));否则编译器会抱怨。

答案 5 :(得分:3)

C中的void指针可以在没有显式强制转换的情况下分配给任何指针。编译器会发出警告,但可以通过将<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <corners android:topLeftRadius="100dp" android:topRightRadius="0dp" android:bottomLeftRadius="100dp" android:bottomRightRadius="0dp" /> <solid android:color="#4F4F4F" /> <size android:width="220dp" android:height="90dp" /> </shape> 类型转换为相应的类型,在重用。对于类型转换,它也可以在 C 中使用,因为 C没有严格的类型检查。但是 C ++是严格的类型检查所以需要在C ++中输入强制转换malloc()