_Generic
在C11中可用,在此之前的C99中,tgmath.h
包含了使用编译器特定的技巧的类似功能。
但是main如何在K&R C或C89 / C90中具有多个签名?
我知道的main()至少有2个函数签名:
1:int main(int argc, const char *argv[]);
2:int main(void);
答案 0 :(得分:3)
但是main如何在K&R C或C89 / C90中具有多个签名?
main
不是在K&R C中本身具有多个签名 。该版本没有您所指的“签名”意义。尽管函数确实对参数的数量和类型有期望,并且仅在满足这些期望的情况下才定义其行为,但函数参数并不构成函数声明的一部分。
C编程语言(Kernighan&Ritchie,1978年)第一版的5.11节中的以下引文可能有启发性:
调用
main
开始执行时,它带有两个参数。
该语句是无条件的:按照K&R的描述,main
(总是)在C中用两个参数调用。编译器可以根据需要或需要执行任何处理未声明这些参数的情况。
这种情况在C90或更高版本的C中(实际上仍然支持K&R样式的函数定义)并没有什么不同。即使main
是用原型声明的,实现也可以执行他们想要或需要做的任何事情。例如,也许他们为标准签名生成代码,并在链接期间对main()
进行递归调用的任何必要修补。或者也许他们为提供的main()
的任何(受支持的)声明生成代码,并以某种特定于OS的包装器对其进行处理。在某些实现中甚至不需要什么特别的东西。
答案 1 :(得分:3)
C标准仅要求实现支持问题中给出的两个签名,
1: int main(int argc, const char *argv[]);
2: int main(void);
对于调用者将参数从调用堆栈中弹出的调用约定,(1)的调用顺序对于(2)可以正常工作-调用者将参数推入堆栈,即被调用者(main
)永远不会使用它们,调用者会将它们从堆栈中删除。
对于调用约定,在该调用约定中,被调用方将参数从调用堆栈中弹出,main
必须根据使用的签名进行不同的编译。在C运行时中使用固定的启动代码实现时,这将是一个问题,因为它不知道如何声明main
。解决该问题的最简单方法是始终对main
使用“ caller pops”调用约定,这实际上就是Microsoft C编译器的工作方式-例如,参见https://docs.microsoft.com/en-us/cpp/build/reference/gd-gr-gv-gz-calling-convention,其中指出:其他调用约定应用于main
时将被忽略。
P.S。
_Generic和tgmath.h对此没有任何影响。
K&R C中没有签名,只有它们的参数名称和可选的类型声明,因此main
只有一种可能的调用约定。
因此,几十年来,这些语言的改变都没有对main
的调用方式产生任何影响。
答案 2 :(得分:0)
C
具有并且没有削弱的功能签名。当然没有特定于参数的参数。大多数编译器在下划线(“ _”)前(并在其后)创建一个穷人的链接器命名空间,从而可以轻松防止符号名冲突。
因此,C运行时启动始终将有一个明确的符号来启动。最常见的是_main
。
start:
;# set up registers
;# set up runtime environment:
;# set up stack, initialize heap, connect stdin, stdout, stderr, etc.
;# obtain environment and format for use with "envp"
;# obtain command line arguments and set up for access with "argv"
push envp
push argv
push argc ; number of arguments in argv
call _main
push r0
call exit
.end start