"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 架构,其中所有对堆栈的推送都由硬件扩展为指针大小。)
在 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 架构,其中所有对堆栈的推送都由硬件扩展为指针大小。)