" int main(void * framep)"?

时间:2017-11-24 06:49:23

标签: c arguments standards main

在C中,main()函数只接受零或两个参数。如果我们提供两个参数,那么第一个参数必须是int类型。

int main(int argc, char *argv[])

但是,我在浏览OpenBSD时看到了以下代码。

int main(void *framep){} 

在C中有效吗?

GCC编译器提供以下警告:

prog.c:3:5: warning: first argument of 'main' should be 'int' [-Wmain]
 int main(void *p) {
     ^~~~
prog.c:3:5: warning: 'main' takes only zero or two arguments [-Wmain]

它的目的是什么?

3 个答案:

答案 0 :(得分:3)

在Linux上,在链接期间,库函数_start将链接到预期存在于代码中的函数main()

传统上,main_start调用int argc, char *argv[],参数数量(包括程序名称)和实际参数(加上尾随NULL)。

但是在其他一些实现中,可能不需要以这种方式调用main,或者出于性能原因,使用不同格式的参数减少来调用它。

main()是我们程序的起始函数,并且传递给argc, argv,但毕竟它只是一个C函数,并且可以通过其他东西传递,只要约定,关于该实施,已知并被接受。

答案 1 :(得分:2)

Oups,这不是一个普通的程序而是一个内核,所以main的正常规则并不真正适用。当程序启动时,不存在传递参数值的环境,并且不会使用main的返回值,因为当内核退出时,不存在任何其他内容。一条评论说,修改定义只是为了应对gcc要求:

  

return int,所以gcc -Werror不会抱怨

在N1256草案中明确表示C11为5.1.2.1独立环境:

  

在独立环境中(C程序执行可能在没有任何情况下执行)   操作系统的好处),程序调用的函数的名称和类型   startup是实现定义的。任何独立的图书馆设施   程序,除了第4条要求的最小集合外,都是实现定义的。

     

独立环境中程序终止的影响是实现定义的。

在内核启动时,没有操作系统仍然存在,因此它实际上在独立环境中运行。这可能意味着还需要使用特殊标志进行编译......

答案 2 :(得分:0)

您使用g++对其进行了编译以获取这些错误,如果您使用gcc进行编译,则无法获取这些错误。

$ gcc test.c

$ g++ test.c
test.c:3:5: warning: first argument of 'int main(void*)' should be 'int' [-Wmain]
 int main(void *framep)
     ^~~~
test.c:3:5: warning: 'int main(void*)' takes only zero or two arguments [-Wmain]

这很重要,因为C不会将参数类型(或数字!)视为函数的类型的一部分(而C ++ ) 。有各种各样的原因,其中包括在C中,调用者清理参数,所以如果他指定了太多,他也会清理它们。在C ++中, callee 清理参数,因此如果他清理了错误的数字,最终会导致堆栈损坏。

为什么你可以选择使用int main(void *framep):在C的调用约定中,参数被压入堆栈,然后调用,接下来返回地址。然后被调用者通常会推送旧的EBP值,然后将堆栈指针移动到EBP中作为"基指针"对于新的堆栈帧。然后移动堆栈指针以分配被调用者中任何自动(本地)变量的空间。即,堆栈如下所示:

Arg n
Arg n-1
...
Arg 1
Return Addr
Old EBP
Callee locals

现在假设我们要检查函数的返回地址,或者读取被推送的前一帧指针(Old EBP)。如果我们在汇编中编写,我们只需相对于当前帧指针(EBP)取消引用。但是我们用C语言写作。获得参考的一种方法是获取第一个参数的地址。也就是&framep,这是Arg1生活在堆栈上的地方。因此(&framep)[-2]应该是void *指向存储的先前帧指针(Old EBP)。

(注意:我假设采用英特尔架构,所有推送到堆栈的内容都由硬件扩展到指针大小。)