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
标准在“前言”中明确提到排除,
- 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组合在一起生成一个可执行文件。这是操作系统能够加载到内存并执行的文件。
我刚刚意识到,当我在 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
标准在“前言”中明确提到排除,
- 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组合在一起生成一个可执行文件。这是操作系统能够加载到内存并执行的文件。