_Generic之前如何支持C对main的不同签名?

时间:2019-04-22 21:44:20

标签: c generics main signature

_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);

3 个答案:

答案 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