在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]
它的目的是什么?
答案 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
)。
(注意:我假设采用英特尔架构,所有推送到堆栈的内容都由硬件扩展到指针大小。)