让我们看看会发生什么。假设这段代码:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("sizeof(int) = %zu\nsizeof(int *) = %zu\n",
sizeof(int), sizeof(int *));
// output:
// sizeof(int) = 4
// sizeof(int *) = 8
int * foo = (int *) malloc(sizeof(int));
return 0;
}
在我的系统上用clang ouch.c 编译它会得到:
ouch.c:5:23: warning: implicitly declaring library function 'malloc' with type 'void *(unsigned long)'
int * foo = (int *) malloc(sizeof(int));
^
ouch.c:5:23: note: include the header <stdlib.h> or explicitly provide a declaration for 'malloc'
在这里,clang 足够聪明,可以注意到我正在调用 malloc,这是一个已知的库函数,并假定(正确的)函数签名 void *(unsigned long)。所以一切都很好。但并不是每个编译器都那么聪明,我也可以欺骗 clang:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("sizeof(int) = %zu\nsizeof(int *) = %zu\n",
sizeof(int), sizeof(int *));
int * foo = (int *) wrapper();
return 0;
}
在一个单独的文件中,我将链接到上面的主文件:
#include <stdlib.h>
void * wrapper(void) {
return malloc(sizeof(int));
}
运行clang wrapper.c ouch2.c 给了我:
ouch2.c:5:23: warning: implicit declaration of function 'wrapper' is invalid in C99 [-Wimplicit-function-declaration]
int * foo = (int *) wrapper();
^
ouch2.c:5:15: warning: cast to 'int *' from smaller integer type 'int' [-Wint-to-pointer-cast]
int * foo = (int *) wrapper();
^
2 warnings generated.
这很好,因为如果这些警告被阅读,那么很容易理解问题的根源并修复它们。但是,如果我忽略它们并保持代码不变,则会发生以下情况:
编译ouch2.c 时,clang 看不到任何wrapper 的声明。由于我从循环中删除了它的智能库函数检测it has no choice but to assume that somewhere this is declared as
int wrapper();
这是一个返回 int 并接受任意数量参数的函数。我们看到了这方面的证据,因为 clang(作为一个智能编译器)警告我关于从(返回的)int 到 int * 的转换及其第二次警告。
将int 转换为int * 并不是什么坏事。 是不好的是假设我们首先得到一个int。假设在wrapper 函数中对malloc 的调用返回了这个值:
0xAABBCCDD11223344
然后会发生什么取决于调用约定。让我们假设它将这个值作为返回值放入某个 64 位寄存器中。
main 中的调用代码需要 int,因此它只从寄存器中读取 32 位(可能是下半部分)并使用它。所以在main,我从wrapper得到这个:
0x11223344
然后将其转换为(64 位)int *,可能导致:
0x0000000011223344
然后用作内存地址。访问这个地址可能(如果你幸运的话)会导致分段错误或者(如果你不是那么幸运)会更改一些随机数据(如果它发生在堆栈上,这会特别有趣,例如更改返回地址) .
所以,最后但并非最不重要的是,如果我把演员排除在外:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("sizeof(int) = %zu\nsizeof(int *) = %zu\n",
sizeof(int), sizeof(int *));
int * foo = wrapper();
return 0;
}
然后用clang wrapper.c ouch3.c 编译我得到:
ouch3.c:5:15: warning: implicit declaration of function 'wrapper' is invalid in C99 [-Wimplicit-function-declaration]
int * foo = wrapper();
^
ouch3.c:5:9: warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' [-Wint-conversion]
int * foo = wrapper();
^ ~~~~~~~~~
2 warnings generated.
也是一个警告,但是一个不同的警告。这种(某种)警告很可能是由您的编译器产生的。
长话短说:clang 对潜在错误发出了很好的警告,并且不强制转换返回值,因为如果您忘记包含 stdlib.h,即使没有使用 clang 编译,您也可以肯定会收到警告:)