Использование нестандартной подписи основного типа в C

Просматривал исходный код sudo, представленный на этом сайте, и наткнулся на эту супер странную подпись типа (дополнительный вопрос: есть ли более похожий на C термин для "подписи типа"?) для main:

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

Я так понимаю, что сам стиль это олдскул K&R. Что меня действительно интересует, так это тот факт, что main принимает бонусный аргумент, char **envp. Почему? sudo является довольно стандартным инструментом командной строки и вызывается как таковой. Как операционная система узнает, что делать, когда она сталкивается с основной функцией, которая не определена с помощью обычного (int argc, char *argv[])?

Много раз я сам был ленив и просто полностью отказывался от аргументов, и любая программа, которую я писал, работала нормально (самая опасная вещь, которая может случиться с C, я знаю :p)

Другая часть моего вопроса заключается в том, какие классные вещи все это позволяет вам делать? У меня есть подозрение, что это очень помогает при встроенном программировании, но, к сожалению, я мало сталкивался с этим и не могу точно сказать. Мне бы хотелось увидеть конкретные примеры


person TheIronKnuckle    schedule 02.06.2013    source источник


Ответы (2)


Это просто указатель на окружение, идентичный

extern char **environ;

Оба были доступны в unix, начиная с версии 7 (в версии 6 не было переменных среды). extern по имени environ был стандартизирован; третий аргумент для main не имел значения. Нет никакой причины использовать 3-arg main, кроме как как своего рода дань моде.

Код настройки процесса, который вызывает 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 в обычной программе на C с двумя аргументами main. А что происходит с argc и argv в программе с int main(void).

Но, как и в случае с самим envp, нет веских причин использовать это в серьезном коде. Проверка типов полезна для вас, и знание того, что вы можете обойти ее, должно сопровождаться знанием того, что вы не должны.

person Community    schedule 02.06.2013
comment
Меня зацепило, когда вы упомянули сборку. Я люблю низкоуровневые вещи :) Также пришлось согласиться, чтобы я вспомнил это странное ключевое слово extern. С нетерпением жду возможности взяться за дело сегодня вечером, должно быть информативно. - person TheIronKnuckle; 03.06.2013

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

С самого первого абзаца:

Другие форматы, зависящие от платформы, также разрешены стандартами C и C++, за исключением того, что в C++ тип возвращаемого значения всегда должен быть int;[3] например, Unix (хотя и не POSIX.1) и Microsoft Windows имеют третий аргумент, задающий значение среда программы, иначе доступная через getenv в stdlib.h:

Гугл твой друг. Кроме того, в этом случае операционной системе не нужно ничего знать о main - это компилятор, который делает работу, и пока это допустимый аргумент, который принимается компилятором, проблем нет.

person Tomer Arazy    schedule 02.06.2013