【发布时间】:2015-01-27 12:42:45
【问题描述】:
我正在使用 Visual Studio 进行代码开发以及何时使用该函数
getche()
编译器给了我这个警告
warning C4013: 'getche' undefined; assuming extern returning int
但是 getche() 函数按预期工作,为什么编译器会显示这样的警告,我该如何消除这个警告?
【问题讨论】:
我正在使用 Visual Studio 进行代码开发以及何时使用该函数
getche()
编译器给了我这个警告
warning C4013: 'getche' undefined; assuming extern returning int
但是 getche() 函数按预期工作,为什么编译器会显示这样的警告,我该如何消除这个警告?
【问题讨论】:
【讨论】:
我想补充一点:纠正此类错误并非易事,因为缺少声明可能会导致更多错误。
以简单的调用foo(4) 为例。假设没有关于此调用如何工作的更多信息。编译器能够从这样的调用中读取什么?
编译器无法从该调用中读取什么:
没错。因为foo 可能在另一个模块中定义,甚至在系统库中。没有声明的符号foo 对编译器来说是完全未知的。因为它只是编译你的代码,但它不负责符号的链接(我说符号是因为它可以包含变量和函数)。
现在,编译器必须做什么?
它需要为每个调用生成指令。根据调用约定,这些指令可能会有很大差异。例如,对于cdecl,调用者 必须将参数压入堆栈,并且在每次调用函数之前。对于stdcall,被调用者必须清理堆栈。
它需要知道参数的类型。好吧,实际上它并不关心类型,而是对象占用的大小。一个 20 字节的结构对象在堆栈上需要 20 个字节,不是吗?而且每个参数都需要放在上面(按值调用)。
而且它需要在栈上为函数的返回值预留足够的内存。因为:无论函数要返回什么,都存储在该内存中。如果函数返回一个 20 字节的结构对象,它需要 20 个字节。到目前为止,一切顺利。
现在,除了返回值之外的任何内容都可以从调用中读取 - 返回值必须由声明提供。如果未提供此声明,编译器将仅在堆栈中保留 sizeof(int)(大部分时间为 4 个字节)作为返回值,并将其余部分留给链接器。
链接器看到符号foo(4 bytes) 并将开始寻找foo(4 bytes) 的任何对应定义。如果它发现了(比如,在你的另一个模块中,或者在你系统的 libc 中,或者作为系统调用包装器),那么链接器就是内容,它将创建可执行文件。
一切都很好,不是吗?嗯,不,不是。
例如,GNU 系统上提供了一个名为memmem 的函数,该函数在定义_GNU_SOURCE 时被激活,然后包含string.h。如果您在包含该函数之前没有碰巧定义_GNU_SOURCE,则memmem 的声明将被跳过,编译器将无法确定该函数的返回值并自动假定为4 个字节。当您在 32 位系统上时,这是“可以的”——但是如果您的程序被编译为 64 位可执行文件并且您传递给 memmem 的 haystack 是一个 64 位指针,它指向 0xFFFFFFFF 上方,即高 32 位你的指针被删除了(好像你已经写了return (pointer&0xFFFFFFFFULL);)。而这个新指针可以指向我所说的“坏涅槃”,与“好涅槃”相对应。 “好涅槃”是NULL,大家可以看出这个指针不应该被解引用。对于指向“坏涅槃”的指针,要做到这一点更加困难。
链接器不会对您的memmem 调用有任何问题,因为我们的系统库仍然知道该函数。不求也能得到,用了就会出卖你。
因此请记住:即使您的编译器需要声明,您仍然希望提供一个。
【讨论】: