在浏览这个站点提供的sudo源代码时,遇到了这个超级奇怪的类型签名(附加问题:是否有一个更像c的术语来表示"类型签名"?)
int
main(argc, argv, envp)
int argc;
char **argv;
char **envp;
{
我知道风格本身就是老式的K&R。我真正感兴趣的是main有一个额外的参数,char **envp
。为什么?Sudo是一个相当标准的命令行工具,可以这样调用。当操作系统遇到一个没有使用通常的(int argc, char *argv[])?
很多时候,我自己也很懒,完全省略了参数,不管我写什么程序,似乎都能正常工作(我知道,这是C语言最危险的事情了:p)
我问题的另一部分是,这一切能让你做什么很酷的事情?我有一种预感,它对嵌入式编程很有帮助,但遗憾的是,我对它的接触很少,所以不能真的说。我很想看到一些具体的例子
它只是一个指向环境的指针,与
相同extern char **environ;
自Version 7以来在unix中都可用(Version 6中没有环境变量)。命名为environ
的extern
得到了标准化;main
的第三个参数没有。没有理由使用3-arg main
,除非作为某种时尚声明。
调用main
的进程设置代码不需要知道main是否期望3个参数,因为在汇编级别,接受2个参数的函数和接受3个参数但不使用第三个参数的函数之间没有区别。或者在一个不接受参数的函数和一个接受两个参数但不使用它们的函数之间,这就是为什么int main(void)
也能工作。
具有非类unix ABI的系统可能需要知道它们调用的是哪种main
。
把这些放到一个文件中:
#include <stdio.h>
int foo(int argc, char **argv)
{
int i;
for(i=0;i<argc;++i)
puts(argv[i]);
return 0;
}
extern int foo(int argc, char **argv, char **envp);
int main(int argc, char **argv)
{
char *foo_args[] = { "foo", "arg", "another arg" };
char *foo_env[] = { "VAR=val", "VAR2=val2" };
foo(3, foo_args, foo_env);
return 0;
}
从跨平台语言律师的角度来看,这是完全错误的。我们向编译器隐瞒了foo
的类型,并传递了比它想要的更多的参数。但是在unix中,它可以工作。额外的参数只是无害地占用堆栈上的一个槽,该槽在函数返回后由调用者适当地计算和清理,或者临时存在于一个寄存器中,在该寄存器中,被调用者不期望找到任何特别的东西,并且调用者希望被调用者清除该寄存器,因此它不介意该寄存器是否在被调用者中被重用用于其他目的。
这正是在具有2参数main的普通C程序中envp
所发生的情况。在int main(void)
的程序中,argc
和argv
发生了什么?
但是就像envp
本身一样,在严肃的代码中没有充分的理由利用这一点。类型检查对您是有好处的,知道可以规避类型检查的同时,您也应该知道不应该。
http://en.wikipedia.org/wiki/Main_function
从第一段开始:
C和c++标准也允许其他平台相关的格式,除了在c++中返回类型必须始终为int;[3]例如,Unix(尽管不是POSIX.1)和Microsoft Windows有第三个参数给出程序的环境,否则可以通过stdlib.h:
中的getenv访问。
谷歌是你的朋友。此外,在这种情况下,操作系统不需要知道任何关于main的信息——是编译器在做这项工作,只要它是编译器接受的有效参数,那么就没有问题。