当前位置: 首页 > 知识库问答 >
问题:

谁检测到拼写错误的函数名?编译器还是链接器?

云焱
2023-03-14

根据C如何编程(Deitel):

像printf和scanf这样的标准库函数不是C编程语言的一部分。例如,编译器无法在printf或scanf中找到拼写错误。当编译器编译printf语句时,它只是在对象程序中为对库函数的“调用”提供空间。但是编译器不知道库函数在哪里——链接器知道。当链接器运行时,它定位库函数,并在对象程序中插入对这些库函数的正确调用。现在目标程序已经完成,可以执行了。因此,链接的程序被称为可执行文件。如果函数名拼写错误,链接器将发现错误,因为它将无法将C程序中的名称与库中任何已知函数的名称相匹配。

由于头文件的存在,这些语句使我感到怀疑。这些文件是在编译之前的预处理阶段包含的,正如我所读到的,编译器使用这些文件。

因此,如果我编写print而不是printf,编译器怎么看不到没有使用该名称声明的函数并抛出错误?

如果正如书中所说,如果编译器不监视头文件,为什么我可以在头文件中声明函数?

共有2个答案

应和悦
2023-03-14

因此,如果我编写print而不是printf,编译器怎么会看不到没有使用该名称声明的函数并抛出错误呢?

编译器可以看到指定函数的标识符在作用域中没有声明。在这些情况下,大多数会发出警告,有些会发出错误,或者可以配置为发出警告。

但这与编译器检测到函数不存在是不同的。编译器检测到函数名尚未声明。如果正确拼写函数名,但不包含函数名的先前声明,编译器将显示相同的行为。

此外,C90和预标准化C允许调用函数,而无需事先声明。此类调用不符合C99或更高版本,但出于兼容性目的,大多数编译器仍然接受它们(通常带有警告)。

如果正如书中所说,如果编译器不监视头文件,为什么我可以在头文件中声明函数?

编译器确实看到了它们,并且确实使用了这些声明。此外,它依赖于原型,如果声明提供了一个,在调用函数时执行适当的参数和返回值转换。此外,如果您使用的函数的参数类型被默认参数提升所改变,那么如果在调用时没有原型在范围内,那么对这些函数的调用是不符合的。未定义的行为结果。

方承弼
2023-03-14

因此,如果我编写print而不是printf,编译器怎么会看不到没有使用该名称声明的函数并抛出错误呢?

你是对的。如果您在任何函数名中输入了错误,任何现代编译器都应该对此进行投诉。例如,gcc投诉以下代码:

$ cat test.c 
int main(void)
{
    unknown();
    return 0;
}
$ gcc -c -Wall -Wextra -std=c11 -pedantic-errors test.c
test.c: In function ‘main’:
test.c:3:5: error: implicit declaration of function ‘unknown’ [-Wimplicit-function-declaration]
     unknown();
     ^

然而,在C语言的C99之前的时代,任何编译器看不到其声明的函数,都会假定该函数返回一个int。因此,如果您是在C99之前的模式下编译,则不需要编译器发出警告。

幸运的是,自C99以来,这个隐式int规则已经从C语言中删除,编译器需要在现代C语言中为它发布诊断(

但是如果您只提供函数的声明或原型:

$ cat test.c 
int unknown(void); /* function prototype */

int main(void)
{
    unknown();
    return 0;
}
$ gcc -c -Wall -Wextra -std=c89 -std=c11 test.c
$

(注意:我使用了-c标志只进行编译而不进行链接;但是如果不使用-c则进行编译。)

没有问题,尽管事实上,unknown()在任何地方都没有定义。这是因为编译器假定unknown()已在其他地方定义,并且只有当链接器试图解析符号unknown时,如果找不到unknown()的定义,它才会抱怨。

通常,头文件只提供必要的声明或原型(在上面的示例中,我直接在文件本身中提供了unknown的原型——它也可以通过头文件完成),而通常不提供实际的定义。因此,作者在这个意义上是正确的,链接器就是发现错误的那个。

 类似资料:
  • My mechanic told me, I couldn’t repair your brakes, so I made your horn louder. — Steven Wright 通常,当出现问题时,我们会在继续运行前先停止它并修复错误。然而, 当以守护进程模式运行时,Puppet 会忽略配置清单的编译错误, 仅从缓存中应用最近一次已知可运行的版本。这个行为是由 usecacheonf

  • 几天前,我在java代码中输入了一个错误,但它编译后运行良好。(尽管结果很奇怪。) 我的代码是: 我无法找到为什么第二个作业没有错误,并且打印了124。(当然,“|”在ASCII码中是124。但为什么是“124”,而不是“|”?) 这是编译器错误吗?还是我还不知道的正确java语法?

  • 自 Electron 8 以来已内置支持 Chromium 拼写检查器。 On Windows and Linux this is powered by Hunspell dictionaries, and on macOS it makes use of the native spellchecker APIs. How to enable the spellchecker? 对于 Electr

  • 问题内容: 我对JVM有一个非常基本的问题:它是编译器还是解释器? 如果它是解释器,那么JVM内部存在的JIT编译器怎么办? 如果两者都不是,那么JVM到底是什么?(我不希望将字节码转换为机器特定的代码等jVM的基本定义。) 问题答案: 首先,让我们对以下术语有一个清晰的认识 是Java编译器-将Java代码编译为 Bytecode 是Java虚拟机-运行/解释/将字节码转换为本 机代码 是即时编

  • 我试过这段代码,它给出了错误的缝合器。我是否使用导入静态com.googlecode.javacv.cpp.opencv_stitching.Stitcher;它给出了相同的错误与缝合器。如果可以,请我有一个解决这个问题的方法。谢谢你。 代码是...

  • 问题内容: 我在运行时遇到错误: 以下: 我将C:\ Program Files(x86)\ Microsoft SDKs \ Windows \ v7.0A \ Include放入PATH,因为它包含BaseTsd.h,但这并不能解决问题。我认为这与案件无关吗? 任何帮助将不胜感激! 问题答案: 它不是PATH,而是包含环境变量。 http://msdn.microsoft.com/zh- CN