【问题标题】:Why does C allow me to call an undeclared function? [duplicate]为什么 C 允许我调用未声明的函数? [复制]
【发布时间】:2018-05-23 19:38:53
【问题描述】:

我有两个文件:test1.ctest2.c,其中包含 main() 函数。

test1.c:

#include <stdio.h>  // printf() function declaration/prototype

// function definition
void say_hello() {
  printf("\tHello, world!\n");
}

test2.c:

#include <stdio.h>  // printf() function declaration/prototype
int main() {
  printf("Inside main()\n");
  say_hello();

  return 0;
}

这是我的makefile:

a.out: test1.o test2.o
    $(CXX) -o a.out test1.o test2.o

test1.o: test1.c
    $(CXX) -c test1.c

test2.o: test2.c
    $(CXX) -c test2.c

现在应该清楚问题出在哪里了:test2.c 中的 main() 函数调用 say_hello() 却没有声明它! 我运行以下命令,以使用 gcc 编译器: make CXX=gcc 我在屏幕上收到此警告:

gcc -c test1.c
gcc -c test2.c
test2.c: In function ‘main’:
test2.c:16:3: warning: implicit declaration of function ‘say_hello’ [-Wimplicit-function-declaration]
   say_hello();
   ^
gcc -o a.out test1.o test2.o

虽然 *.o 文件已编译并链接到可执行文件中。这很奇怪。想象一下当我运行 a.out 文件时我的惊讶,我看到 main() 成功调用了 say_hello(),这是一个没有在 main 的翻译单元内声明的函数,好像根本没有问题一样!我的理由是因为say_hello() 之前没有在test2.c 中声明,所以根本不允许main() 调用它。请注意,我已将 cmets 添加到 #include &lt;stdio.h&gt;。这些预处理器指令包括函数声明/原型,它们将它们的范围扩展到相应的 *.c 文件中。这就是我们能够使用它们的原因。没有函数声明/原型 == 在那个翻译单元中没有作用域,或者直到现在我都这么认为。

然后我将上面的代码编译为 C++ 代码: make CXX=g++

我在屏幕上看到这个错误:

test2.c: In function ‘int main()’:
test2.c:16:13: error: ‘say_hello’ was not declared in this scope
   say_hello();
             ^
makefile:18: recipe for target 'test2.o' failed
make: *** [test2.o] Error 1

g++ 做它应该做的事情,并停止编译过程。但是 gcc 没有这样做!这是怎么回事?它是 C 编程语言的优势吗?是编译器的问题吗?

【问题讨论】:

  • 选择一种语言。
  • 这是 C 编程语言的一项优势 - 是的。 C 很天真,认为程序员知道自己在做什么。
  • @Galaxy 声明或多或少只是告诉您某物具有什么类型(C 和 C++ 中的函数类型通常称为签名)。它实际上与多个翻译单元没有任何关系,尽管它对此也很有用。
  • @Galaxy 这个问题与这两种语言无关,因为 C++ 不允许这样做。 ;-)
  • 这是一个向后兼容的东西。 C 诞生时没有原型。程序员应该知道如何调用每个函数,并且假设每个未声明的函数都返回 int (并采用未指定数量的(提升的)参数,这是没有原型的结果)。

标签: c++ c scope function-declaration


【解决方案1】:

简单,因为 C 允许调用未声明的函数而 C++ 不允许。无论哪种方式,gcc 都会向您发出警告,您可能需要认真对待警告。

【讨论】:

  • 但是为什么 C 允许调用未声明的函数,不像 C++?
  • 因为它可以。 C++ 是一种不同的语言,有一些更严格的规则。
  • -Werror (-Werror=implicit-function-declaration)。那更严重。 :)
  • @Galaxy 历史原因,来自只有一种存储类型的 B 语言——在 C 中变成了int。虽然你需要声明你在块中使用的函数,比如main() { extrn /* sic */ printn, char, putchar; … },但是我不确定这是否是强制性的。
  • 考虑制作程序的两个部分:编译和链接。为什么不尝试在test1.c 中删除say_hello 看看会发生什么?程序编译得很好,但是当链接器尝试搜索say_hello(因为它是在test2.c 中调用的)时,它会失败。因此,表达 C 和 C++ 之间这种差异的一种方式是,C 将引发链接器错误,而 C++ 将引发编译器错误。
猜你喜欢
  • 2020-10-20
  • 2018-08-15
  • 1970-01-01
  • 1970-01-01
  • 2019-10-27
  • 1970-01-01
  • 2012-08-03
  • 2014-09-15
相关资源
最近更新 更多