"int main(void *framep)" 的目的是什么?

What is the purpose of "int main(void *framep)"?

在 C 中,main() 函数只接受零或两个 arguments.If 我们提供两个参数,那么第一个参数必须是 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]

这样做的目的是什么?

在您提供的link中,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 Program startup 1

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters: int main(void) { /* ... */ }

or

with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

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

or equivalent) or in some other implementation-defined manner.

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

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

然而,在其他一些实现中,可能没有必要以这种方式调用 main,或者出于性能原因,使用不同的格式使用更少的参数调用它。

main()是我们程序的起始函数,传给argc, argv,但毕竟只是一个C函数,可能传别的东西,只要约定俗成,就那个实现,已知并被接受。

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

return int, so gcc -Werror won't complain

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

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined. Any library facilities available to a freestanding program, other than the minimal set required by clause 4, are implementation-defined.

The effect of program termination in a freestanding environment is implementationdefined.

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

你用 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 的调用约定中,参数被压入堆栈,然后进行调用,其中放置 return 地址下一个。然后,被调用者通常会压入 EBP 的旧值,然后将堆栈指针移动到 EBP 作为新堆栈帧的 "base pointer"。然后移动堆栈指针以分配被调用者中任何自动(本地)变量的space。即,堆栈如下所示:

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

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

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