_Generic
可用于C11,在此之前在C99中,tgmath.h
包含使用编译器特定黑客攻击的类似功能。
但是主要在K&R C或C89 / C90中有多个签名?
我知道的main()至少有2个函数签名:
1:int main(int argc, const char *argv[]);
2:int main(void);
但是主要在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()
提供的任何(支持的)声明生成代码,并在某种特定于操作系统的包装器中处理它。在某些实现中甚至可能没有什么特别的东西。
C标准只要求实施支持问题中给出的两个签名,
1: int main(int argc, const char *argv[]);
2: int main(void);
对于调用者从调用栈中弹出参数的调用约定,(1)的调用序列适用于(2) - 调用者将参数推送到堆栈,被调用者(main
)从不使用它们,调用者从堆栈中删除它们。
对于调用约定,其中被调用者从调用堆栈弹出参数,main
必须根据使用的签名进行不同的编译。这在C运行时中具有固定启动代码的实现中会出现问题,因为它不知道如何声明main
。最简单的处理方法是始终使用main
的“调用者弹出”调用约定,这实际上是微软的C编译器的工作方式 - 例如,参见https://docs.microsoft.com/en-us/cpp/build/reference/gd-gr-gv-gz-calling-convention,其中指出其他调用约定在应用于main
。
附:
main
只有一个可能的调用约定。因此,几十年来这些语言变化都没有对main
的调用产生任何影响。
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