编译器给出 - 警告C4013:' getche'不确定的;假设extern返回int

时间:2015-01-27 12:42:46

标签: c getch

我使用Visual Studio进行代码开发以及使用函数

  

getche()

编译器给了我这个警告

 warning C4013: 'getche' undefined; assuming extern returning int

但getche()函数正如预期的那样工作,为什么编译器会显示这样的警告,以及如何消除此警告?

2 个答案:

答案 0 :(得分:3)

正如您的编译器告诉您的那样,getche尚未定义。

通常,您会包含一个标题(例如#include <conio.h>),它将定义与以下内容类似的函数:

int getche(void);

C语言有一条规则允许使用之前未定义的函数。假设这些函数采用它们传递的确切参数类型(无,void)并返回int。由于此功能(这是evil™)以及getche确实因链接器设置而被链接的事实,您的程序实际上可以正常工作。

请注意,我们已弃用getche,您应该使用_getche

答案 1 :(得分:1)

我想补充一件小事:纠正这些错误并非易事,因为缺少声明会导致更多错误。

以简单的电话foo(4)为例。假设没有关于此调用如何工作的进一步信息。什么是编译器能够从这样的调用中读取?

  1. 参数数量(非常明显)。
  2. 参数的类型(4通常扩展为4字节值,64位计算机上的4LL(gcc)为8字节值)。
  3. 应该在堆栈上推送参数的顺序(调用约定 - 大部分时间它是C约定,但它取决于您的编译器设置)。
  4. 编译器无法从该调用中读取什么:

    1. 通话返回什么样的价值。
    2. 没错。因为foo可能在另一个模块中定义,或者甚至在系统库中定义。没有声明的符号foo对于编译器来说完全是未知的。因为它只是编译你的代码,但它不负责你的符号的链接(我说符号因为这可以包括变量函数)。

      现在,编译器必须做什么

      1. 需要为每个调用生成说明。根据调用约定,这些指令可能有很大不同。例如,对于cdecl调用者必须在每次调用函数之前推送堆栈上的参数。使用stdcall时,被调用者必须清理堆栈。

      2. 需要知道参数的类型。嗯,实际上它并不关心类型,而是关注对象占用的大小。一个20字节的struct对象需要20个字节,不是吗?每个参数都需要放在它上面(按值调用)。

      3. 它需要在堆栈上保留足够的内存以获取函数的返回值。因为:无论函数将返回什么,都存储在该内存中。如果函数返回一个20字节的struct对象,则需要20个字节。到目前为止,非常好。

      4. 现在,可以从调用中读取任何但返回值 - 返回值必须由声明提供。如果未提供此声明,编译器将仅在堆栈上保留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调用没有任何问题,因为我们的系统库仍然会知道该函数。你得到它甚至没有要求它,但如果你使用它会背叛你。

        所以请记住:即使你的编译器没有需要声明,你仍然想提供一个。