这两个对我来说都很好。第一个错误很常见,因为您在使用所述相同(例如malloc(size_t))中声明的函数之前忘记#include <stdlib.h>,我确实没有忘记这样做.
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 和数据指针的大小不相同,因此您最终会产生一些不良后果:
- 演员表隐藏了真正的错误。
- 虚假指针是由返回的真实指针的一半位制造的。
- 作为一种残酷的伤口盐,分配的内存被泄漏,因为在任何地方都没有引用它的有效指针(你只是通过只保留一半而破坏了唯一的指针)。
- 可能是最不受欢迎的,如果在实现时编译代码将表现出正常行为
sizeof(int) == sizeof(int**)。
所以你在你的 32 位 Debian 机器上构建它,一切看起来都很好。你把作业交给在他的 64 位 Mac 上构建它的教授,然后它崩溃了,你不及格,不及格,大学辍学,在接下来的十年里抚摸这只猫,同时在你妈妈的地下室看 Seinfeld 重播想知道出了什么问题。哎哟。
不要将施法视为银弹。它不是。在 C 语言中,远 需要它的频率低于人们使用它的频率,如果在错误的地方使用它,可能会隐藏灾难性的错误。如果您在代码中发现没有强制转换就无法编译的地方,再看一遍。除非您绝对、肯定地确定演员阵容是正确的,否则它的可能性错误。
在这种情况下,它隐藏了真正的错误,即您忽略了向编译器提供足够的信息以了解 malloc 的真正作用。