这两个对我来说都很好。第一个错误很常见,因为您忘记#include <stdlib.h>
在使用所述相同(例如malloc(size_t)
)中声明的函数之前,我没有忘记这样做。
C 有一些有趣的编译时行为,其中包括调用以前从未见过的函数的能力(原型定义和实现都没有)。在遇到这样的调用时,C 假定函数是:
- 返回的东西
int
- 接受未知数量的参数,因此调用者可以传递它想要的任何东西(包括错误的东西)。
例如,函数被隐式假设为以下形式:
int func();
通常你甚至不会注意到,除了来自编译器的警告,这些警告会报告以下内容:
Warning: implicit declaration of `func` assumed to return `int`
如果你在球上,你的警告级别会在启用警告错误的情况下出现,并且会抓住这一点。
但如果你不这样做呢?如果从函数返回的“事物”不能由实现中的数据大小以内容表示int
怎么办?例如,如果int
是 32 位,但数据指针是 64 位怎么办?例如,假设char *get_str()
在您不包含的某个头文件中声明,并在您编译并与程序链接的 .c 文件中实现,如下所示:
#include <stdio.h>
// Note: NO prototype for get_str
int main()
{
char *s = get_str();
printf("String: %s\n", s);
return 0;
}
好吧,编译器应该呕吐,告诉你int
并且char*
不兼容(在它警告你之后不久就get_str
假定你返回int
)。但是,如果您通过告诉编译器以char*
一种或另一种方式来强制编译器怎么办:
#include <stdio.h>
// Note: NO prototype for get_str
int main()
{
char *s = (char*)get_str(); // NOTE: added cast
printf("String: %s\n", s);
return 0;
}
现在,如果没有启用错误警告,您将收到一个隐式声明警告,仅此而已。代码将编译。但它会运行吗?如果sizeof(int)
!= sizeof(char*)
,(32 位与 64 位)可能不会。从返回的值get_str
是一个 64 位指针,但调用者假设只返回 32 位,然后将其强制为 64 位指针。简而言之,演员隐藏了错误并打开了未定义行为的潘多拉魔盒。
那么所有这些与您的代码有什么关系呢?通过不包括<stdlib.h>
编译器不知道是什么malloc
。所以它假设它的形式是:
int malloc();
然后,通过将结果转换为(int**)
您告诉编译器“无论结果如何,都使其成为int**
”。在链接时,_malloc
找到(没有像 C++ 那样通过名称修改的参数签名),连接起来,你的程序就准备好了。但是在您的平台上int
,数据指针的大小不一样,因此您最终会产生一些不良后果:
- 演员隐藏了真正的错误。
- 虚假指针是由实际返回指针的一半位制成的。
- 作为对伤口的残忍剂量,分配的内存被泄漏,因为在任何地方都没有引用它的有效指针(你只是通过只保留它的一半而破坏了唯一的指针)。
- 可能是最不受欢迎的,如果在 where 的实现上编译代码将表现出正常行为
sizeof(int) == sizeof(int**)
。
所以你在你的 32 位 Debian 机器上构建它,一切看起来都很好。你把你的作业交给教授,他在他的 64 位 Mac 上构建它,然后它崩溃了,你不及格,不及格,辍学,在接下来的十年里抚摸这只猫,同时在你妈妈的地下室看 Seinfeld 重播想知道出了什么问题。哎哟。
不要把铸造当作银弹。它不是。在 C 语言中,需要它的频率远低于人们使用它的频率,如果用在错误的地方,可能会隐藏灾难性的错误。如果您在代码中发现没有硬转换就无法编译的地方,请再看一遍。除非您绝对、肯定地确定演员阵容是正确的,否则很可能是错误的。
在这种情况下,它隐藏了真正的错误,即您忽略了向编译器提供足够的信息以了解malloc
真正的作用。