c语言 - 编译器给出 - 警告 C4013:'getche'未定义;假设外部返回 int



我正在使用Visual studio进行代码开发以及何时使用该函数

格切()

编译器给了我这个警告

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

但是getche()函数的工作符合预期,为什么编译器会显示这样的警告,我怎样才能消除这个警告?

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

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

int getche(void);

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

请注意,getche已弃用,应改用_getche

我想补充的一件小事:更正此类错误并非易事,因为缺少声明可能会导致进一步的错误。

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

  1. 参数的数量(很明显)。
  2. 参数的类型(4 通常扩展为 4 字节值,64 位机器 (gcc) 上的 4LL 扩展为 8 字节值)。
  3. 参数应该在
  4. 堆栈上推送的顺序(调用约定 - 大多数情况下是 C 约定,但这取决于您的编译器设置)。

编译器无法从该调用中读取的内容:

  1. 调用返回哪种值。

没错。因为foo可能在另一个模块中定义,甚至在系统库中定义。符号foo,没有声明,编译器完全不知道。因为它只是编译你的代码,但它不负责你的符号的链接(我说符号是因为它可以包括变量函数)。

现在,编译器必须做什么?

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

  2. 它需要知道参数是什么类型。好吧,实际上它并不关心类型,而是对象占用的大小。一个 20 字节的结构对象在堆栈上需要 20 个字节,不是吗?并且每个参数都需要放在上面(按值调用)。

  3. 并且它需要在堆栈上为函数的返回值保留足够的内存。因为:无论函数要返回什么,都存储在该内存中。如果函数返回一个 20 字节的结构对象,则需要 20 个字节。目前为止,一切都好。

现在,除了返回值之外的任何内容都可以从调用中读取 - 返回值必须由声明提供。如果未提供此声明,编译器将只在堆栈上保留sizeof(int)(大多数情况下为 4 个字节)作为返回值,其余部分留给链接器。

链接器看到符号foo(4 bytes),并将开始查找foo(4 bytes)的任何相应定义。如果它找到它(例如,在您的另一个模块中,或在您的系统的 libc 中,或作为 syscall 包装器),则链接器是内容,它将创建可执行文件。

一切都很好,不是吗?嗯,不,不是。

例如,GNU 系统上提供了一个名为 memmem 的函数,该函数在定义_GNU_SOURCE时激活,然后包含string.h。如果在包含该函数之前没有碰巧定义_GNU_SOURCE,则将跳过memmem声明,编译器将无法确定函数具有的返回值,并将自动假定 4 个字节。当您在 32 位系统上时,这是"OK"的 - 但是如果您的程序被编译为 64 位可执行文件,并且您传递给 memmem 的 haystack 是一个指向 0xFFFFFFFF 上方的 64 位指针,则指针的上部 32 位正在被擦除(就好像您已经写了return (pointer&0xFFFFFFFFULL);一样)。这个新指针可以指向我所说的"坏涅槃",与"好涅槃"相对应。"好涅槃"是NULL,大家看得出来,这个指针不应该被取消引用。对于指向"坏涅槃"的指针,要做到这一点更加困难。

链接器在memmem调用时不会有任何问题,因为我们的系统库仍然知道该函数。即使不要求它,你也能得到它,但如果你使用它,它会背叛你。

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

最新更新