C究竟如何运行?

How does C exactly run?

我刚刚意识到,当我在 C 中定义一个函数并使用它时,我可以先使用它再定义函数,或者先定义它再使用。例如,

int mult (int x, int y)
{
  return x * y;
}

int main()
{
  int x;
  int y;
  scanf( "%d", &x );
  scanf( "%d", &y );
  printf( "The product of your two numbers is %d\n", mult( x, y ) );
}

int main()
    {
      int x;
      int y;
      scanf( "%d", &x );
      scanf( "%d", &y );
      printf( "The product of your two numbers is %d\n", mult( x, y ) );
    }   

int mult (int x, int y)
    {
      return x * y;
    }

都 运行 就好了。但是,在 Python 中,第二个代码将失败,因为它需要先定义 mult(x,y) 才能使用它,并且 Python 从上到下执行(据我所知)。显然,在 C 中情况并非如此,因为第二个 运行 就可以了。那么 C 代码实际上是如何流动的?

嗯,严格来说,第二个代码是无效的 C。

它利用编译器的灵活性允许函数的隐式声明,这在 C 标准中是不允许的。

C11标准在“前言”中明确提到排除

  1. Major changes in the second edition included:

...

  • remove implicit function declaration

你必须

  • Forward declare 函数。
  • 在使用之前定义函数(如代码片段 1)。

在您的编译器中启用警告,您的编译器应该会生成一些警告消息让您了解这个问题

对于第二个代码,您应该使用前向声明。这意味着首先声明该函数,以便编译器知道您将使用该函数。现在您的代码是根据 C 编译器的灵活性执行的。

Python编译器不够灵活所以会编译失败

您说这两种代码都可以正常工作。好吧,事实并非如此。第二个片段将在我的编译器中显示错误,如果它编译正确,则不应使用它。

  • 它使 C 规则无效。
  • 当需要太多用户定义的函数时,提前声明函数很有用。

正如其他人指出的那样,例程在使用之前应该声明,尽管在使用之前不需要定义用过的。此外,旧版本的 C 允许一些例程的隐式声明,一些编译器仍然这样做,尽管这在很大程度上已经过时了。

至于 C 如何支持函数定义前的调用,C 程序首先 翻译 成某种可执行格式,然后程序 已执行.

在翻译过程中,C 编译器读取、分析和处理整个程序。任何对尚未定义的函数的引用都被记录为需要在程序中解决的事情。在准备最终可执行文件的过程中,linker 遍历所有处理过的数据,找到函数定义,并通过插入被调用例程的地址(或其他信息)来解析引用。

最常见的是,C 编译器将源代码翻译成目标模块。目标模块包含程序的机器语言指令。它还包含源代码中定义的程序的任何数据,以及有关编译器在分析源代码时发现的未解析引用的信息。多个源文件可以分别翻译成多个目标模块。有时这些翻译是由不同的人在不同的时间完成的。一家公司可能会制作一个软件库,它是将源文件翻译成目标模块并将它们打包成库文件的结果。然后,软件开发人员将编译他们自己的源文件和 link 生成的目标模块以及库中的目标模块。

多个目标模块可以link组合在一起生成一个可执行文件。这是操作系统能够加载到内存并执行的文件。