我使用Visual Studio进行代码开发以及使用函数
getche()
编译器给了我这个警告
warning C4013: 'getche' undefined; assuming extern returning int
但getche()函数正如预期的那样工作,为什么编译器会显示这样的警告,以及如何消除此警告?
答案 0 :(得分:3)
正如您的编译器告诉您的那样,getche
尚未定义。
通常,您会包含一个标题(例如#include <conio.h>
),它将定义与以下内容类似的函数:
int getche(void);
C语言有一条规则允许使用之前未定义的函数。假设这些函数采用它们传递的确切参数类型(无,void
)并返回int
。由于此功能(这是evil™)以及getche
确实因链接器设置而被链接的事实,您的程序实际上可以正常工作。
答案 1 :(得分:1)
我想补充一件小事:纠正这些错误并非易事,因为缺少声明会导致更多错误。
以简单的电话foo(4)
为例。假设没有关于此调用如何工作的进一步信息。什么是编译器能够从这样的调用中读取?
编译器无法从该调用中读取什么:
没错。因为foo
可能在另一个模块中定义,或者甚至在系统库中定义。没有声明的符号foo
对于编译器来说完全是未知的。因为它只是编译你的代码,但它不负责你的符号的链接(我说符号因为这可以包括变量和函数)。
现在,编译器必须做什么 ?
需要为每个调用生成说明。根据调用约定,这些指令可能有很大不同。例如,对于cdecl
,调用者必须在每次调用函数之前推送堆栈上的参数。使用stdcall
时,被调用者必须清理堆栈。
需要知道参数的类型。嗯,实际上它并不关心类型,而是关注对象占用的大小。一个20字节的struct对象需要20个字节,不是吗?每个参数都需要放在它上面(按值调用)。
它需要在堆栈上保留足够的内存以获取函数的返回值。因为:无论函数将返回什么,都存储在该内存中。如果函数返回一个20字节的struct对象,则需要20个字节。到目前为止,非常好。
现在,可以从调用中读取任何但返回值 - 返回值必须由声明提供。如果未提供此声明,编译器将仅在堆栈上保留sizeof(int)
(大多数时间为4个字节)作为返回值,并将其余部分保留给链接器。
链接器会看到符号foo(4 bytes)
,并将开始查找foo(4 bytes)
的任何相应定义。如果它发现(例如,在您的另一个模块中,或在您的系统的libc中,或作为系统调用包装器),那么链接器就是内容,它将创建可执行文件。
一切都很好,不是吗?嗯,不,不是。
例如,在GNU系统上提供了一个名为memmem
的函数,该函数在包含_GNU_SOURCE
之前定义string.h
时被激活。如果在包含该函数之前没有定义_GNU_SOURCE
,则将跳过memmem
的声明,并且编译器将无法确定该函数的返回值并将自动假设为4个字节。当你在32位系统上时这是“OK” - 但是如果你的程序被编译为64位可执行文件而你传递给memmem
的haystack是一个指向0xFFFFFFFF
以上的64位指针,指针的高32位正在被删除(就好像你已经写了return (pointer&0xFFFFFFFFULL);
)。而这个新指针可以指向我所谓的“坏天堂”,与“好的必杀技”相对应。 “好的必杀技”是NULL,每个人都可以看到这个指针不应该被解除引用。要做到这一点,指向“坏天堂”的指针就更难了。
链接器对memmem
调用没有任何问题,因为我们的系统库仍然会知道该函数。你得到它甚至没有要求它,但如果你使用它会背叛你。
所以请记住:即使你的编译器没有需要声明,你仍然想提供一个。