我想询问功能签名,调用和定义的顺序
喜欢,计算机看哪一个,第二个和第三个
所以:
#include <iostream>
using namespace std;
void max(void);
void min(void);
int main() {
max();
min();
return;
}
void max() {
return;
}
void min() {
return;
}
所以这就是我的想法,
计算机将转到main并查看函数调用,然后它将查看
函数签名,最后,它将查看定义。
是不是?
感谢
答案 0 :(得分:1)
如您所知,编译器将程序转换为机器代码(通过几个中间步骤)。以下是在Windows 8上以调试模式在Visual Studio 2012上编译时main()
的机器代码的反汇编:
int main() {
00C24400 push ebp # Setup stack frame
00C24401 mov ebp,esp
00C24403 sub esp,0C0h
00C24409 push ebx
00C2440A push esi
00C2440B push edi
00C2440C lea edi,[ebp-0C0h] # Fill with guard bytes
00C24412 mov ecx,30h
00C24417 mov eax,0CCCCCCCCh
00C2441C rep stos dword ptr es:[edi]
max();
00C2441E call max (0C21302h) # Call max
min();
00C24423 call min (0C2126Ch) # Call min
return 0;
00C24428 xor eax,eax
}
00C2442A pop edi # Restore stack frame
00C2442B pop esi
00C2442C pop ebx
00C2442D add esp,0C0h
00C24433 cmp ebp,esp
}
00C24435 call __RTC_CheckEsp (0C212D5h) # Check for memory corruption
00C2443A mov esp,ebp
00C2443C pop ebp
00C2443D ret
具体细节因编译器,编译器和操作系统而异。如果min()或max()具有参数或返回值,则它们将根据体系结构进行传递。关键点在于编译器已经确定了参数和返回值是什么,并创建了刚刚传递或接受它们的机器代码。
如果您希望帮助调试或进行低级别调用,可以了解更多详细信息,但请注意所发出的机器代码可能变化很大。例如,下面是在发布模式下在同一系统上编译的相同代码(即优化开启):
return 0;
01151270 xor eax,eax
}
01151272 ret
如您所见,它已检测到min()
和max()
什么都不做,并完全删除了它们。由于现在没有堆栈帧可供设置和恢复,所以不用了,只留下一条指令将eax设置为0然后返回(因为返回值在eax寄存器中)。
答案 1 :(得分:1)
是不是?
没有
您需要了解函数声明和函数定义之间的区别,编译,链接和执行之间的区别,以及非虚函数和虚函数之间的区别。
功能声明
这是一个函数声明:void max(void);
。它没有告诉编译器有关该函数的功能。它的作用是告诉编译器如何调用函数以及如何解释结果。当编译器正在编译某个函数的主体时,将其称为函数A,编译器不需要知道其他函数的作用。它需要知道的是如何处理A调用的函数。编译器可能会生成汇编代码或与C ++函数调用相对应的某种中间语言。或者它可能会拒绝您的C ++代码,因为您的代码没有意义。
确定代码是否有意义是这些函数声明的另一个关键目的。这在C ++中尤为重要,因为多个函数可以具有相同的名称。如果编译器不了解这些函数,编译器将如何知道要调用的函数中的哪一个max
?当您的C ++代码调用某个函数时,编译器必须使用其中一个函数声明找到一个最佳匹配(可能涉及类型转换)。如果编译器根本找不到匹配项,或者它找到多个匹配项但无法将其中一个匹配为最佳匹配项,则代码没有意义。
当编译器找到最佳匹配时,生成的代码将采用对该函数的未定义外部引用的调用形式。该函数存在的地方不是编译器的工作。
功能定义
void max(void)
是函数声明。相应的void max() {...}
是该函数的定义。当编译器正在处理void max() {...}
时,它不必担心其他函数调用它。它只需要担心处理void max() {...}
。此函数的主体成为插入到某个编译对象文件中的汇编语言或中间语言代码。编译器标记此生成代码的入口点的地址标记为。
汇编与链接
到目前为止,我已经谈到了编译器的功能。它生成与您的C ++代码相对应的低级代码块。由于那些外部引用,生成的代码尚未准备好进入黄金时间。解析那些未定义的外部引用是链接器的工作。链接器是从多个目标文件,多个库构建可执行文件的。它跟踪它将这些代码块放在可执行文件中的位置。那些未定义的外部参考怎么样?如果链接器已将该引用放在可执行文件中,则链接器只需填充该引用的占位符。如果链接器没有遇到该引用的定义,它会将引用和占位符放在仍未解析的引用列表中。每次链接器向可执行文件添加一块代码时,它都会检查该列表以查看它是否可以修复任何仍未解析的引用。最后,您要么已经解决了所有引用,要么仍然会有一些优秀的引用。后者是一个错误。前者意味着您有可执行文件。
<强>执行强>
当你的代码运行时,那些函数调用实际上只是一些堆栈管理,它围绕着与邪恶的goto
语句相当的机器语言。没有检查你的函数声明;那些在代码执行时甚至不存在。返回?那也是goto
。
非虚拟与虚拟功能
我上面所说的涉及非虚函数。虚拟功能确实发生运行时调度。该运行时调度与检查函数声明无关。这些虚函数可能是另一个问题的问题。
最后一件事:
摆脱using namespace std;
的习惯。把它想象成吸烟。这是一个坏习惯。