Войти
Windows. Программы. Железо. Интернет. Безопасность. Мой компьютер
  • Прошивка huawei g510 0200 скачать и установить
  • Что означает версия ядра 3
  • Минимальное и максимальное качество звука
  • Скачать сервисы google play на андроид последняя
  • Как отключить тачпад на ноутбуке dell
  • Информация о размерах и весе устройства, представленная в разных единицах измерения
  • Разница между int main() и int main (void)? Аргументы функции main Передача указателей функции Си

    Разница между int main() и int main (void)? Аргументы функции main Передача указателей функции Си

    Borland С++ поддерживает три аргумента main(). Первые два - это традиционные argc и argv. Это единственные аргументы функции main(), определяемые стандартом ANSI С. Они позволяют передавать аргументы командной строки в программу. Аргументы командной строки - это информация, следующая за именем программы в командной строке операционной системы. Например, когда программа компилируется с помощью строчного компилятора Borland, набирается, как правило, bcc имя_ программы

    Где имя_программы - это программа, которую необходимо откомпилировать. Имя программы передается компилятору в качестве аргумента.

    Параметр argc содержит число аргументов командной строки и является целым числом. Он всегда равен, по крайней мере, 1, поскольку имя программы квалифицируется как первый аргумент. Параметр argv - это указатель на массив символьных указателей. Каждый элемент данного массива указывает на аргумент командной строки. Все аргументы командной строки - это строки. Все числа конвертируются программой во внутренний формат. Следующая программа выводит «Hello», а затем имя пользователя, если его набрать прямо за именем программы:

    #include

    {
    if(argc!=2)
    {
    printf ("You forgot to type your name\n");
    return 1;
    }
    printf("Hello %s", argv);
    return 0;
    }

    Если назвать данную программу name, а имя пользователя Сергей, то для запуска программы следует набрать:
    name Сергей.
    В результате работы программы появится:
    «Hello Сергей».

    Аргументы командной строки должны отделяться пробелами или табуляциями. Запятые, точки с запятыми и им подобные символы не рассматриваются как разделители. Например:

    Состоит из трех строк, в то время как

    Herb,Rick,Fred

    Это одна строка - запятые не являются разделителями.

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

    "this is a test"

    Важно правильно объявить argv. Наиболее типичным методом является:

    Пустые скобки указывают на то, что массив не имеет фиксированной длины. Можно получить доступ к отдельным элементам с помощью индексации argv. Например, argv указывает на первую строку, всегда содержащую имя программы. argv указывает на следующую строку и так далее.

    Ниже приведен небольшой пример по использованию аргументов командной строки. Он отсчитывает в обратном порядке от значения, указанного в командной строке, и при достижении нуля подает сигнал. Обратим внимание, что первый аргумент содержит число, преобразованное в целое число с использованием стандартной функции atoi(). Если в качестве второго аргумента присутствует строка "display", то на экране будет отображаться сам счетчик.

    /* программа отсчета */

    #include
    #include
    # include
    int main(int argc, char *argv)
    {
    int disp, count;
    if(argc<2)
    {
    printf("You must enter the length of the count\n");
    printf ("on the command line. Try again.\n");
    return 1;
    }
    if (argc==3 && !strcmp(argv,"display")) disp = 1;
    else disp = 0;
    for(count=atoi(argv); count; -count)
    if (disp) printf("%d ", count);
    printf("%c", "\a"); /* на большинстве компьютеров это звонок */
    return 0;
    }

    Обратим внимание, что если не указаны аргументы, появляется сообщение об ошибке. Это наиболее типично для программ, использующих аргументы командной строки для выдачи инструкций, если была попытка запустить программу без правильной информации.

    Для доступа к отдельным символам командной строки следует добавить второй индекс к argv. Например, следующая программа выводит все аргументы, с которыми она вызывалась, по одному символу за раз:

    #include
    int main(int argc, char *argv)
    {
    int t, i;
    for(t=0; t {
    i = 0;
    while(argv[t][i])
    {
    printf("%c", argv[t][i]);
    }
    printf (" ");
    }
    return 0;
    }

    Надо помнить, что первый индекс предназначен для доступа к строке, а второй - для доступа к символу строки.

    Обычно argc и argv используются для получения исходных команд. Теоретически можно иметь до 32767 аргументов, но большинство операционных систем не позволяют даже близко подойти к этому. Обычно данные аргументы используются для указания имени файла или опций. Использование аргументов командной строки придает программе профессиональный вид и допускает использование программы в командных файлах.

    Если подсоединить файл WILDARGS.OBJ, поставляемый с Borland С++, то можно будет использовать шаблоны в аргументах типа *.EXE. (Borland С++ автоматически обрабатывает шаблоны и соответствующим образом увеличивает argc.) Например, если подсоединить к следующей программе WILDARGS.OBJ, она выдаст, сколько файлов соответствует имени указанного в командной строке файла:

    /* Скомпонуйте данную программу с WILDARGS.OBJ */

    #include
    int main(int argc, char *argv)
    {
    register int i;
    printf("%d files match specified name\n", argc-1);
    printf("They are: ");
    for(i=1; i printf ("%s ", argv[i]);
    return 0;
    }

    Если назвать данную программу WA, затем запустить ее как указано ниже, получим число файлов, имеющих расширение ЕХE, и список имен этих файлов:

    Помимо argc и argv Borland С++ также предоставляет третий аргумент командной строки -env. Параметр env позволяет программе получить доступ к информации о среде операционной системы. Параметр env должен следовать за argc и argv и объявляется следующим образом:

    Как можно видеть, env объявляется так же, как и argv. Так же, как и argv, это указатель на массив строк. Каждая строка - это строка среды, определенная операционной системой. Параметр env не имеет аналога параметра argc, который сообщал бы, сколько имеется строк среды. Вместо этого последняя строка среды нулевая. Следующая программа выводит все строки среды, определенные на текущий момент в операционной системе:

    /* данная программа выводит все строки окружения */

    #include
    int main(int argc, char *argv, char *env)
    {
    int t;
    for(t=0; env[t]/ t++)
    printf("%s\n", env[t]);
    return 0;
    }

    Обратим внимание, что хотя argc и argv не используются программой, они должны присутствовать в списке параметров. С не знает имена параметров. Вместо этого их использование определяется по порядку объявления параметров. Фактически можно обозвать параметр как угодно. Поскольку argc, argv и env - это традиционные имена, то лучше их использовать и далее, чтобы любой человек, читающий программу, мог мгновенно понять, что это аргументы функции main().

    Для программ типичной задачей является поиск значения, определенного в строке среды. Например, содержимое строки PATH позволяет программам использовать пути поиска. Следующая программа демонстрирует, как найти строки, объявляющие стандартные пути поиска. Она использует стандартную библиотечную функцию strstr(), имеющую следующий прототип:

    Char *strstr(const char *str1, const char *str2);

    Функция strstr() ищет строку, на которую указывает str1 в строке, на которую указывает str2. Если такая строка найдена, то возвращается указатель на первое положение. Если не найдено соответствий, то функция возвращает NULL.

    /* программа ищет среди строк окружения строку, содержащую PATH */

    #include
    #include
    int main (int argc, char *argv, char *env)
    {
    int t;
    for(t=0; env[t]; t++)
    {
    if(strstr(env[t], "PATH"))
    printf("%s\n", env[t]);
    }
    return 0;
    }

    Для чего нужны функции в C?

    Функции в Си применяются для выполнения определённых действий в рамках общей программы. Программист сам решает какие именно действия вывести в функции. Особенно удобно применять функции для многократно повторяющихся действий.

    Простой пример функции в Cи

    Пример функции в Cи:

    #include #include int main(void) { puts("Functions in C"); return EXIT_SUCCESS; }

    Это очень простая программа на Си. Она просто выводит строку «Functions in C». В программе имеется единственная функция под названием main. Рассмотрим эту функцию подробно. В заголовке функции, т.е. в строке

    int – это тип возвращаемого функцией значения;

    main - это имя функции;

    (void) - это перечень аргументов функции. Слово void указывает, что у данной функции нет аргументов;

    return – это оператор, который завершает выполнение функции и возвращает результат работы функции в точку вызова этой функции;

    EXIT_SUCCESS - это значение, равное нулю. Оно определено в файле stdlib.h;

    часть функции после заголовка, заключенная в фигурные скобки

    {
    puts("Functions in C");
    return EXIT_SUCCESS;
    }

    называют телом функции.

    Итак, когда мы работаем с функцией надо указать имя функции, у нас это main, тип возвращаемого функцией значения, у нас это int, дать перечень аргументов в круглых скобках после имени функции, у нас нет аргументов, поэтому пишем void, в теле функции выполнить какие-то действия (ради них и создавалась функция) и вернуть результат работы функции оператором return. Вот основное, что нужно знать про функции в C.

    Как из одной функции в Cи вызвать другую функцию?

    Рассмотрим пример вызова функций в Си:

    /* Author: @author Subbotin B.P..h> #include int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; }

    Запускаем на выполнение и получаем:

    В этом примере создана функция sum, которая складывает два целых числа и возвращает результат. Разберём подробно устройство этой функции.

    Заголовок функции sum:

    int sum(int a, int b)

    здесь int - это тип возвращаемого функцией значения;

    sum - это имя функции;

    (int a, int b) - в круглых скобках после имени функции дан перечень её аргументов: первый аргумент int a, второй аргумент int b. Имена аргументов являются формальными, т.е. при вызове функции мы не обязаны отправлять в эту функцию в качестве аргументов значения перемнных с именами a и b. В функции main мы вызываем функцию sum так: sum(d, e);. Но важно, чтоб переданные в функцию аргументы совпадали по типу с объявленными в функции.

    В теле функции sum, т.е. внутри фигурных скобок после заголовка функции, мы создаем локальную переменную int c, присваиваем ей значение суммы a плюс b и возвращаем её в качестве результата работы функции опрератором return.

    Теперь посмотрим как функция sum вызывается из функции main.

    Вот функция main:

    Int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; }

    Сначала мы создаём две переменных типа int

    Int d = 1; int e = 2;

    их мы передадим в функцию sum в качестве значений аргументов.

    int f = sum(d, e);

    её значением будет результат работы функции sum, т.е. мы вызываем функцию sum, которая возвратит значение типа int, его-то мы и присваиваем переменной f. В качестве аргументов передаём d и f. Но в заголовке функции sum

    int sum(int a, int b)

    аргументы называются a и b, почему тогда мы передаем d и f? Потому что в заголовке функций пишут формальные аргументы, т.е. НЕ важны названия аргументов, а важны их типы. У функции sum оба аргумента имеют тип int, значит при вызове этой функции надо передать два аргумента типа int с любыми названиями.

    Ещё одна тонкость. Функция должна быть объявлена до места её первого вызова. В нашем примере так и было: сначала объявлена функция sum, а уж после мы вызываем её из функции main. Если функция объявляется после места её вызова, то следует использовать прототип функции.

    Прототип функции в Си

    Рассмотрим пример функциив Си:

    /* Author: @author Subbotin B.P..h> #include int sum(int a, int b); int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; } int sum(int a, int b) { int c = 0; c = a + b; return c; }

    В этом примере функция sum определена ниже места её вызова в функции main. В таком случае надо использовать прототип функции sum. Прототип у нас объявлен выше функции main:

    int sum(int a, int b);

    Прототип - это заголовок функции, который завершается точкой с запятой. Прототип - это объявление функции, которая будет ниже определена. Именно так у нас и сделано: мы объявили прототип функции

    int f = sum(d, e);

    а ниже функции main определяем функцию sum, которая предварительно была объявлена в прототипе:

    Int sum(int a, int b) { int c = 0; c = a + b; return c; }

    Чем объявление функции в Си отличается от определения функции в Си?

    Когда мы пишем прототип функции, например так:

    int sum(int a, int b);

    то мы объявляем функцию.

    А когда мы реализуем функцию, т.е. записываем не только заголовок, но и тело функции, например:

    Int sum(int a, int b) { int c = 0; c = a + b; return c; }

    то мы определяем функцию.

    Оператор return

    Оператор return завершает работу функции в C и возвращает результат её работы в точку вызова. Пример:

    Int sum(int a, int b) { int c = 0; c = a + b; return c; }

    Эту функцию можно упростить:

    Int sum(int a, int b) { return a + b; }

    здесь оператор return вернёт значение суммы a + b.

    Операторов return в одной функции может быть несколько. Пример:

    Int sum(int a, int b) { if(a > 2) { return 0;// Первый случай; } if(b < 0) { return 0;// Второй случай; } return a + b; }

    Если в примере значение аргумента a окажется больше двух, то функция вернет ноль (первый случай) и всё, что ниже комментария «// Первый случай;» выполнятся не будет. Если a будет меньше двух, но b будет меньше нуля, то функция завершит свою работу и всё, что ниже комментария «// Второй случай;» выполнятся не будет.

    И только если оба предыдущих условия не выполняются, то выполнение программы дойдёт до последнего оператора return и будет возвращена сумма a + b.

    Передача аргументов функции по значению

    Аргументы можно передавать в функцию C по значению. Пример:

    /* Author: @author Subbotin B.P..h> #include int sum(int a) { return a += 5; } int main(void) { puts("Functions in C"); int d = 10; printf("sum = %d\n", sum(d)); printf("d = %d", d); return EXIT_SUCCESS; }

    В примере, в функции main, создаём переменную int d = 10. Передаём по значению эту переменную в функцию sum(d). Внутри функции sum значение переменной увеличивается на 5. Но в функции main значение d не изменится, ведь она была передана по значению. Это означает, что было передано значение переменной, а не сама переменная. Об этом говорит и результат работы программы:

    т.е. после возврата из функции sum значеие d не изменилось, тогда как внутри функции sum оно менялось.

    Передача указателей функции Си

    Если в качестве аргумента функции передавать вместо значения переменной указатель на эту переменную, то значение этой переменной может меняться. Для примера берём программу из предыдущего раздела, несколько изменив её:

    /* Author: @author Subbotin B.P..h> #include int sum(int *a) { return *a += 5; } int main(void) { puts("Functions in C"); int d = 10; printf("sum = %d\n", sum(&d)); printf("d = %d", d); return EXIT_SUCCESS; }

    В этом варианте программы я перешел от передачи аргумента по значению к передаче указателя на переменную. Рассмотрим подробнее этот момент.

    printf("sum = %d\n", sum(&d));

    в функцию sum передается не значение переменной d, равное 10-ти, а адрес этой переменной, вот так:

    Теперь посмотрим на функцию sum:

    Int sum(int *a) { return *a += 5; }

    Аргументом её является указатель на int. Мы знаем, что указатель - это переменная, значением которой является адрес какого-то объекта. Адрес переменной d отправляем в функцию sum:

    Внутри sum указатель int *a разыменовывается. Это позволяет от указателя перейти к самой переменной, на которую и указывает наш указатель. А в нашем случае это переменная d, т.е. выражение

    равносильно выражению

    Результат: функция sum изменяет значение переменной d:

    На этот раз изменяется значение d после возврата из sum, чего не наблюдалось в предыдущм пункте, когда мы передавали аргумент по значению.

    C/C++ в Eclipse

    Все примеры для этой статьи я сделал в Eclipse. Как работать с C/C++ в Eclipse можно посмотреть . Если вы работаете в другой среде, то примеры и там будут работать.

    Пожалуйста, приостановите работу AdBlock на этом сайте.

    Итак, зачем нужны пользовательские функции? Пользовательские функции нужны для того, чтобы программистам было проще писать программы.

    Помните, мы говорили о парадигмах программирования, а точнее о структурном программировании. Основной идеей там было то, что любую программу можно можно написать используя только три основных конструкции: следование, условие и цикл. Теперь к этим конструкциям мы добавим ещё одну – «подпрограммы» – и получим новую парадигму процедурное программирование» .

    Отличие лишь в том, что отдельные кусочки нашей основной программы (в частности, повторяющиеся) мы будем записывать в виде отдельных функций (подпрограмм, процедур) и по мере необходимости их вызывать. По сути, программа теперь будет описывать взаимодействие различных функций.

    Итак, в этом уроке мы подробно обсудим то, как функции устроены изнутри. А также научимся создавать свои собственные пользовательские функции.

    Как устроены функции

    Вспомним информацию с первого урока. Все функции, в том числе и те, которые пишет пользователь, устроены сходным образом. У них имеется две основных составных части: заголовок функции и тело функции.

    Листинг 1.

    Int main(void){ // заголовок функции // в фигурных скобках записано тело функции }

    С телом функции всё ясно: там описывается алгоритм работы функции. Давайте разберёмся с заголовком. Он состоит из трёх обязательных частей:

    • тип возвращаемого значения;
    • имя функции;
    • аргументы функции.

    Сначала записывается тип возвращаемого значения, например, int , как в функции main . Если функция не должна возвращать никакое значение в программу, то на этом месте пишется ключевое слово void . Казалось бы, что раз функция ничего не возвращает, то и не нужно ничего писать. Раньше, кстати, в языке Си так и было сделано, но потом для единообразия всё-таки добавили. Сейчас современные компиляторы будут выдавать предупреждения/ошибки, если вы не укажете тип возвращаемого значения.
    В некоторых языках программирования функции, которые не возвращают никакого значения, называют процедурами (например, pascal). Более того, для создания функций и процедур предусмотрен различный синтаксис. В языке Си такой дискриминации нет.

    После типа возвращаемого значения записывается имя функции. Ну а уж после имени указываются типы и количество аргументов, которые передаются в функцию.

    Давайте посмотрим на заголовки уже знакомых нам функций.

    Листинг 2.

    // функция с именем srand, принимающая целое число, ничего не возвращает void srand(int) //функция с именем sqrt, принимающая вещественное число типа float, возвращает вещественное число типа float float sqrt(float) //функция с именем rand, которая не принимает аргументов, возвращает целое число int rand(void) //функция с именем pow, принимающая два аргумента типа double, возвращает вещественное число типа double double pow(double, double)

    Как создать свою функцию

    Для того чтобы создать свою функцию, необходимо её полностью описать. Тут действует общее правило: прежде чем использовать – объяви и опиши, как должно работать. Для этого вернёмся к схеме структуры программы на языке Си, которая у нас была в самом первом уроке. Отметим на ней те места, где можно описывать функции.

    Рис.1 Уточнение структуры программы. Объявление функций.

    Как видите, имеется аж два места, где это можно сделать.

    Давайте посмотрим на пример, который иллюстрируют создание пользовательской функции вычисления максимального из двух чисел.

    Листинг 3.

    #include // объявляем пользовательскую функцию с именем max_num // вход: два целочисленных параметра с именами a и b // выход: максимальное из двух аргументов int max_num(int a, int b){ int max = b; if (a > b) max = a; return max; } //основная программа int main(void) { int x = 0, y = 0; int m = 0; scanf("%d %d", &x, &y); m = max_num(x,y); printf("max(%d,%d) = %d\n",x,y,m); return 0; }

    Давайте я подробно опишу, как будет работать эта программа. Выполняется тело функции main . Создются целые переменные x , y и m . В переменные x и y считываются данные с клавиатуры. Допустим мы ввели 3 5 , тогда x = 3 , y = 5 . Это вам всё и так должно быть понятно. Теперь следующая строчка

    Листинг 4.

    M = max_num(x,y);

    Переменной m надо присвоить то, что находится справа от знака = . Там у нас указано имя функции, которую мы создали сами. Компьютер ищет объявление и описание этой функции. Оно находится выше. Согласно этому объявлению данная функция должна принять два целочисленных значения. В нашем случае это значения, записанные в переменных x и y . Т.е. числа 3 и 5 . Обратите внимание, что в функцию передаются не сами переменные x и y , а только значения (два числа), которые в них хранятся. То, что на самом деле передаётся в функцию при её вызове в программе, называется фактическими параметрами функции.

    Теперь начинает выполняться функция max_num . Первым делом для каждого параметра, описанного в заголовке функции, создается отдельная временная переменная. В нашем случае создаются две целочисленных переменных с именами a и b . Этим переменным присваиваются значения фактических параметров. Сами же параметры, описанные в заголовке функции, называются формальными параметрами. Итак, формальным параметрам a и b присваиваются значения фактических параметров 3 и 5 соответственно. Теперь a = 3 , b = 5 . Дальше внутри функции мы можем работать с этими переменными так, как будто они обычные переменные.

    Создаётся целочисленная переменная с именем max , ей присваивается значение b . Дальше проверяется условие a > b . Если оно истинно, то значение в переменной max следует заменить на a .

    Далее следует оператор return , который возвращает в вызывающую программу (функцию main ) значение, записанное в переменной max , т.е. 5 . После чего переменные a , b и max удаляются из памяти. А мы возвращаемся к строке

    Листинг 5.

    M = max_num(x,y);

    Функция max_num вернула значение 5 , значит теперь справа от знака = записано 5 . Это значение записывается в переменную m. Дальше на экран выводится строчка, и программа завершается.

    Внимательно прочитайте последние 4 абазаца ещё раз, чтобы до конца уяснить, как работает программа.

    А я пока расскажу, зачем нужен нижний блок описания функций. Представьте себе, что в вашей программе вы написали 20 небольших функций. И все они описаны перед функцией main . Не очень-то удобно добираться до основной программы так долго. Чтобы решить эту проблему, функции можно описывать в нижнем блоке.

    Но просто так перенести туда полностью код функции не удастся, т.к. тогда нарушится правило: прежде чем что-то использовать, необходимо это объявить. Чтобы избежать подобной проблемы, необходимо использовать прототип функции.

    Прототип функции полностью повторяет заголовок функции, после которого стоит ; . Указав прототип в верхнем блоке, в нижнем мы уже можем полностью описать функцию. Для примера выше это могло бы выглядеть так:

    Листинг 6.

    #include int max_num(int, int); int main(void) { int x =0, y = 0; int m = 0; scanf("%d %d", &x, &y); m = max_num(x,y); printf("max(%d,%d) = %d\n",x,y,m); return 0; } int max_num(int a, int b){ int max = b; if (a > b) max = a; return max; }

    Всё очень просто. Обратите внимание, что у прототипа функции можно не указывать имена формальных параметров, достаточно просто указать их типы. В примере выше я именно так и сделал.

    9 ответов

    Некоторые из функций языка C начинаются как хаки, которые только что сработали.

    Одной из этих функций является несколько подписей для основного, а также списков аргументов переменной длины.

    Программисты заметили, что они могут передавать дополнительные аргументы функции, и с их компилятором ничего плохого не происходит.

    Это так, если вызывающие соглашения таковы, что:

    • Вызывающая функция очищает аргументы.
    • Самые левые аргументы ближе к вершине стека или к базе фрейма стека, так что ложные аргументы не делают недействительной адресацию.

    Один набор условных вызовов, который подчиняется этим правилам, является передачей параметров на основе стека, в результате чего вызывающий пользователь выдает аргументы, и они помещаются справа налево:

    ;; pseudo-assembly-language ;; main(argc, argv, envp); call push envp ;; rightmost argument push argv ;; push argc ;; leftmost argument ends up on top of stack call main pop ;; caller cleans up pop pop

    В компиляторах, где этот тип соглашения о вызове имеет значение, ничего особого не нужно делать для поддержки двух типов main или даже дополнительных типов. main может быть функцией без аргументов, и в этом случае он не обращает внимания на элементы, которые были перенесены в стек. Если это функция из двух аргументов, она находит argc и argv в качестве двух верхних элементов стека. Если это вариант с тремя аргументами, ориентированный на платформу, с указателем среды (общим расширением), это тоже будет работать: он найдет третий аргумент как третий элемент из верхней части стека.

    И поэтому фиксированный вызов работает для всех случаев, позволяя связать один, фиксированный модуль запуска с программой. Этот модуль может быть записан на C, как функция, напоминающая это:

    /* I"m adding envp to show that even a popular platform-specific variant can be handled. */ extern int main(int argc, char **argv, char **envp); void __start(void) { /* This is the real startup function for the executable. It performs a bunch of library initialization. */ /* ... */ /* And then: */ exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere)); }

    Другими словами, этот начальный модуль всегда вызывает основной аргумент с тремя аргументами. Если main не принимает никаких аргументов или только int, char ** , он работает нормально, а также если он не принимает никаких аргументов из-за соглашений о вызовах.

    Если бы вы делали такие вещи в своей программе, это было бы непереносимо и считалось бы поведением undefined по ISO C: объявлением и вызовом функции одним способом и определением ее в другой. Но трюк запуска компилятора не должен быть переносимым; он не руководствуется правилами для переносных программ.

    Но предположим, что вызывающие соглашения таковы, что они не могут работать таким образом. В этом случае компилятор должен обрабатывать main специально. Когда он замечает, что он компилирует функцию main , он может генерировать код, который совместим, например, с тремя аргументами.

    То есть вы пишете это:

    Int main(void) { /* ... */ }

    Но когда компилятор видит это, он, по сути, выполняет преобразование кода, так что функция, которую он компилирует, выглядит примерно так:

    Int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore) { /* ... */ }

    за исключением того, что имена __argc_ignore не существуют буквально. Такие имена не вводятся в вашу область действия, и никаких предупреждений о неиспользуемых аргументах не будет. Преобразование кода заставляет компилятор испускать код с правильной связью, которая знает, что ему нужно очистить три аргумента.

    Другая стратегия реализации для компилятора или, возможно, линкера для пользовательской генерации функции __start (или того, что она называется), или, по крайней мере, выбрать один из нескольких предварительно скомпилированных альтернатив. В объектном файле может храниться информация о том, какая из поддерживаемых форм main используется. Компонент может посмотреть эту информацию и выбрать правильную версию модуля запуска, которая содержит вызов main , который совместим с определением программы. В реализациях C обычно имеется только небольшое количество поддерживаемых форм main , поэтому этот подход возможен.

    Компиляторы для языка C99 всегда должны в некоторой степени относиться к main , чтобы поддерживать хак, что если функция завершается без оператора return , поведение выглядит так, как если бы выполнялось return 0 . Это, опять же, можно рассматривать с помощью преобразования кода. Компилятор замечает, что скомпилирована функция с именем main . Затем он проверяет, может ли конец тела потенциально достижим. Если это так, он вставляет return 0;

    Нет никакой перегрузки main даже в С++. Основная функция - это точка входа для программы, и должно существовать только одно определение.

    Для стандартного C

    Для размещенной среды (обычной), стандарт C99 говорит:

    5.1.2.2.1 Запуск программы

    Функция, вызванная при запуске программы, называется main . Реализация не объявляет прототипа для этой функции. Это должно быть определенный с типом возврата int и без параметров:

    Int main(void) { /* ... */ }

    или с двумя параметрами (называемыми здесь argc и argv , хотя любые имена могут использоваться, поскольку они являются локальными для функции, в которой они объявляются):

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

    или эквивалент; 9) или каким-либо другим способом реализации.

    9) Таким образом, int можно заменить на имя typedef, определенное как int , или тип argv можно записать как char **argv , и и так далее.

    Для стандартного С++:

    3.6.1 Основная функция

    1 Программа должна содержать глобальную функцию main, которая является назначенным началом программы. [...]

    2 Реализация не должна предопределять основную функцию. Эта функция не должна быть перегружена . Он должен имеют тип возвращаемого типа int, но в противном случае его тип определяется реализацией. Все реализации должны допускать оба следующих определения main:

    Int main() { /* ... */ }

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

    В стандарте С++ явно говорится: "Он [основная функция] должен иметь тип возвращаемого типа int, но в противном случае его тип определяется реализацией" и требует тех же двух сигнатур, что и стандарт C.

    В размещенной среде (среда C, которая также поддерживает библиотеки C) - операционная система вызывает main .

    В не-размещенной среде (один предназначен для встроенных приложений) вы всегда можете изменить точку входа (или выйти) вашей программы, используя директивы предварительного процессора, такие как

    #pragma startup #pragma exit

    Если приоритет является необязательным интегральным числом.

    Запуск Pragma выполняет функцию перед тем, как основной (приоритетный) и выход прагмы выполняет функцию после основной функции. Если существует более одной директивы запуска, приоритет определяет, что будет выполняться первым.

    Это одна из странных асимметрий и специальных правил языка C и С++.

    По-моему, он существует только по историческим причинам, и нет реальной серьезной логики. Обратите внимание, что main является особенным также по другим причинам (например, main в С++ не может быть рекурсивным, и вы не можете взять его адрес, а на C99/С++ вы можете опустить окончательный оператор return).

    Обратите внимание, что даже в С++ это не перегрузка... либо программа имеет первую форму, либо имеет вторую форму; он не может иметь обоих.

    Что необычно для main не в том, что его можно определить более чем одним способом, он может быть определен только одним из двух способов.

    main - пользовательская функция; реализация не объявляет прототип для него.

    То же самое верно для foo или bar , но вы можете определять функции с этими именами так, как вам нравится.

    Различие заключается в том, что main вызывается реализацией (среда выполнения), а не только вашим собственным кодом. Реализация не ограничивается обычной семантикой вызова функции C, поэтому она может (и должна) иметь дело с несколькими вариантами, но не требует обработки бесконечно многих возможностей. Форма int main(int argc, char *argv) допускает аргументы командной строки, а int main(void) в C или int main() в С++ - это просто удобство для простых программ, которые не требуют обработки аргументов командной строки.

    Что касается того, как компилятор справляется с этим, это зависит от реализации. Большинство систем, вероятно, имеют соглашения о вызовах, которые делают две формы эффективно совместимыми, и любые аргументы, переданные в main , определенные без параметров, игнорируются. В противном случае компилятору или компоновщику не составит труда специально обработать main . Если вам интересно, как это работает в вашей системе, вы можете посмотреть некоторые списки сборок.

    И, как и многие другие на C и С++, детали в значительной степени являются результатом истории и произвольных решений, сделанных разработчиками языков и их предшественников.

    Обратите внимание, что оба C и С++ допускают другие определения, определенные для реализации для main , но редко есть веские основания для их использования. А для автономных реализаций (таких как встроенные системы без ОС) точка входа в программу определяется реализацией и необязательно даже называется main .

    main - это просто имя для начального адреса, решенного компоновщиком, где main - имя по умолчанию. Все имена функций в программе - это начальные адреса, где начинается функция.

    Я знаю, что поток старый, но несколько лет назад этот вопрос беспокоил меня, поэтому я хотел бросить свою половину процента (если это).

    Я всегда рассматриваю функции C, как если бы они фиксировали количество аргументов независимо от контекста, если только они не используют va_args. То есть, я верю, что ВСЕГДА имеют прототип:

    Int main(int argc, char **argv).

    даже если аргументы не переданы, функция имеет эти аргументы в стеке, потому что основная функция не имеет перегрузки функций.

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

    Высказывание int main() просто означает, что я знаю, что функция может иметь параметры, но я их не использую, поэтому я пишу int main().

    Высказывание int main (void) говорит о том, что main НЕКОТОРНО не имеет аргументов и подразумевает наличие двух разных прототипов функций:

    Int main(void); int main(int argc, char **argv);

    Так как C не имеет функции перегрузки, это несколько вводит меня в заблуждение, и я не доверяю коду с основным (void) в нем. Я бы не стал, если main NEVER принял какие-либо параметры, и в этом случае main (void) будет полностью в порядке.

    ПРИМЕЧАНИЕ. В некоторых реализациях есть больше параметров в основном, чем argc и argv, таких как env, но это меня не беспокоит, потому что я знаю, что я не говорю прямо, что это только два параметра, но это минимальные параметры, и все в порядке, чтобы иметь больше, но не меньше. Это противоречит прямому утверждению int main (void), который кричит на меня, поскольку ЭТОТ ФУНКЦИЯ НЕТ ПАРАМЕТРОВ, что неверно, поскольку это так, они просто опускаются.

    Вот мой базовый код:

    /* sample.c - build into sample. */ #include int main(void) { int _argc = *((int *)2686800); char ***_pargv = (char ***)2686804; int i; for (i = 1; i < _argc; ++i) { printf("%s ", (*_pargv)[i]); } return 0; }

    ./sample У меня явно есть аргументы

    У функции явно есть аргументы, переданные ей, несмотря на то, что она явно не говорит о том, что она не набирает void в прототип функции.

    Как указано выше:

    (6.7.6.3p10) Частный случай неименованного параметра типа void как только элемент в списке указывает, что функция не имеет параметров.

    Таким образом, говоря, что функция имеет пустоту как аргумент, но фактически наличие аргументов в стеке является противоречием.

    Моя точка зрения заключается в том, что аргументы все еще существуют, поэтому явное утверждение о том, что main не содержит аргументов, является нечестным. Честным способом было бы сказать int main(), который не утверждает ничего о том, сколько параметров у него есть, только о том, сколько параметров вам нужно.

    ПРИМЕЧАНИЕ2: _argc, _pargv зависят от системы, чтобы найти ваши значения, которые вы должны найти, запустив эту программу:

    /* findargs.c */ #include int main(int argc, char **argv) { printf("address of argc is %u.\n", &argc); printf("address of argv is %u.\n", &argv); return 0; }

    Эти значения должны оставаться правильными для вашей конкретной системы.