2

正在浏览本网站上提供的 sudo 的源代码,并且遇到了这个超级奇怪的类型签名(额外的问题:“类型签名”是否有更像 C 的术语?):

int
main(argc, argv, envp)
    int argc;
    char **argv;
    char **envp;
{

我知道风格本身就是 oldskool K&R。我真正感兴趣的是 main 正在接受一个额外的论点,char **envp. 为什么?sudo 是一个相当标准的命令行工具,因此被调用。当遇到一个没有用通常定义的主函数时,操作系统如何知道该怎么做?(int argc, char *argv[])?

很多时候,我自己都很懒惰,只是完全放弃了论点,而我编写的任何程序似乎都可以正常工作(我知道 C 可能发生的最危险的事情:p)

我的另一部分问题是,这一切能让你做什么很酷的事情?我有一种预感,它对嵌入式编程很有帮助,但遗憾的是,我对此的接触很少,也不能真正说出来。我很想看到一些具体的例子

4

2 回答 2

1

http://en.wikipedia.org/wiki/Main_function

从第一段开始:

C 和 C++ 标准也允许其他依赖于平台的格式,除了在 C++ 中,返回类型必须始终为 int;[3] 例如,Unix(尽管不是 POSIX.1)和 Microsoft Windows 有第三个参数,给出程序的环境,否则可通过 stdlib.h 中的 getenv 访问:

谷歌是你的朋友。此外,在这种情况下,操作系统不需要了解有关 main 的任何信息 - 是编译器完成工作,只要它是编译器接受的有效参数,就没有问题。

于 2013-06-02T22:20:24.690 回答
1

它只是一个指向环境的指针,与

extern char **environ;

从版本 7 开始,两者都在 unix 中可用(在版本 6 中没有环境变量)。extern命名environ标准化;第三个论点main没有。main除了作为某种时尚声明外,没有理由使用 3-arg 。

调用的进程设置代码main不需要知道 main 是否需要 3 个参数,因为在汇编级别上,接受 2 个参数的函数和接受 3 个参数但不使用第三个参数的函数之间没有区别。或者在一个不带参数的函数和一个带 2 个参数但不使用它们的函数之间,这就是为什么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中,它可以工作。额外的参数只是无害地占用堆栈上的一个槽,该槽在函数返回后由调用者正确解释和清理,或者暂时存在于被调用者不希望找到任何特定内容的寄存器中,并且调用者期望被调用者破坏,因此它不介意寄存器是否在被调用者中被重用于其他目的。

这正是envp具有 2-arg main 的普通 C 程序中发生的情况。argc以及argv在程序中会发生什么int main(void)

但就像envp它本身一样,没有充分的理由在严肃的代码中利用这一点。类型检查对你有好处,知道你可以逃避它应该知道你不应该

于 2013-06-02T22:59:58.073 回答