现在在人们开始将这个标记为复制之前,我已经阅读了以下所有内容,其中没有一个提供我正在寻找的答案:
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
的返回值而可能发生的编译或运行时错误,还是仅仅是一个城市传奇?
编辑我在这个问题上遇到了两个写得很好的论点:
答案 0 :(得分:65)
您不会遇到编译器错误,而是编译器警告。作为您引用的来源(特别是first one),当使用强制转换而不包括{{1}时,您可以获得不可预测的运行时错误 }
所以你身边的错误不是演员,而是忘记包括stdlib.h
。编译器可能会认为stdlib.h
是一个返回malloc
的函数,因此将int
实际返回的void*
指针转换为malloc
,然后将指针类型转换为指针类型显式演员。在某些平台上,int
和指针可能会占用不同的字节数,因此类型转换可能会导致数据损坏。
幸运的是,现代编译器会发出警告,指出您的实际错误。请参阅您提供的int
输出:它警告您隐式声明(gcc
)与内置int malloc(int)
不兼容。因此即使没有malloc
,gcc
似乎也知道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>
类型转换为相应的类型,在malloc()
。