“int main(void *framep)”的目的是啥?

Posted

技术标签:

【中文标题】“int main(void *framep)”的目的是啥?【英文标题】:What is the purpose of "int main(void *framep)"?“int main(void *framep)”的目的是什么? 【发布时间】:2018-05-08 03:24:32 【问题描述】:

在 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]

它的目的是什么?

【问题讨论】:

反正也没用过。 见:C11 Standard §5.1.2.2.1 Program startup (draft n1570)。另见:See What should main() return in C and C++? 【参考方案1】:

在 Linux 上,在链接期间,库函数 _start 将链接到预计会出现在您的代码中的函数 main()

然后,传统上您的main_start 调用,带有int argc, char *argv[]、参数数量(包括程序名称)和实际参数(加上结尾的NULL)。

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

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

【讨论】:

是的,例如 VS accepts 3 arguments。好答案! ;)【参考方案2】:

哎呀,这不是一个普通的程序而是一个内核,所以 main 的普通规则并不真正适用。程序启动时,不存在传递参数值的环境,也不会使用 main 的返回值,因为内核退出时,不存在其他任何东西。一条评论说,修改了定义以仅满足 gcc 要求:

返回 int,所以 gcc -Werror 不会抱怨

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

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

独立环境中程序终止的效果是由实现定义的。

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

【讨论】:

【参考方案3】:

在您提供的链接中,framep 没有在 main 函数中使用。

不,这不是标准的。

如您所见,GCC 会发出警告,但值得注意的是,clang 会引发错误:

error: first parameter of 'main' (argument count) must be of type 'int'
int main(void *framep) 
    ^
1 error generated.

来自Standard:

5.1.2.2.1 程序启动1

程序启动时调用的函数名为main。实现没有为此声明原型 功能。它应定义为返回类型为 int 并且没有 参数:int main(void) /* ... */

带有两个参数(这里称为 argc 和 argv,尽管可以使用任何名称,因为它们对于 它们被声明):

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

或等效)或以其他一些实现定义的方式。

【讨论】:

当心,5.1.2.2.1 是 5.1.2.2 托管环境的一部分,当内核在独立环境中运行时,因为在内核本身之前不存在操作系统。 你从一个不合理的开始。它的实现定义了main 的参数应该是独立的;改用(void) 可能是个错误 正确@M.M 谢谢,已更正! Serge Ballesta,我知道看到你的答案了,太好了! 5.1.2.2.1末尾的“或以某种其他实现定义的方式”是什么意思?【参考方案4】:

你用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 中,caller 会清理参数,所以如果他指定太多,他也会清理它们。在 C++ 中,被调用者会清理参数,所以如果他清理了错误的数字,你最终会得到一个损坏的堆栈。

关于您可能选择使用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)。

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

【讨论】:

以上是关于“int main(void *framep)”的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

既是3的倍数又是5的倍数都有哪些

一个三位数既是3的倍数,又是5的倍数。这样的三位数最小是啥

数组的创建,及数组的方法

cnn中的步长的目的和重要性是啥

物质的运动

多态的好处??