Почему после main () есть определения функций? - PullRequest
1 голос
/ 27 мая 2011

Я предположил, что одна из наиболее часто используемых системных функций (ls) одной из самых известных ОС (linux), написанная одним из самых авторитетных программистов (Ричардом Столлманом), может быть примером действительно хорошо написанного кода.

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

Не прокомментирует ли это какой-нибудь опытный программист на Си?

Ответы [ 5 ]

9 голосов
/ 27 мая 2011

Нет абсолютно ничего плохого в том, что здесь сделал Столлман.

Язык C допускает forward объявление функции, которая будет определена впоследствии.

Это имеет много преимуществ и не должно рассматриваться как плохое поведение, а скорее очень хорошее поведение.

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

В случае этой реализации ls он просто предварительно объявил функции, которые он 'Я буду использовать в main(), но если вы внимательно посмотрите, главная функция появится первой.Это наиболее вероятно для удобства чтения, так что вам не нужно прокручивать весь путь вниз, чтобы достичь точки входа в программу.

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

int my_function( char *text); // function declaration, no implementation
int main( int argc, char **argv)
{
   return my_function(argv[0]); // use of the declared function
}

// actual function definition / implementation
int my_function( char *text )
{
   printf("%s\n", text);
}

Редактировать : после более внимательного изучения кода вы увидите, что Столлман не объявлял все свои функции вперед.У него также довольно странная манера определения функций.Я приписываю это старости кода, который датируется 1985 годом, когда компилятор C не был так хорошо определен, как сегодня.Должно быть, он разрешил использование функции такого типа до объявления или определения .

Последнее, но не менее важное: можно найти недавнюю версию ls исходного кода.здесь: http://coreutils.sourcearchive.com/documentation/7.4/ls_8c-source.html,
с гораздо большим C99-совместимым кодированием, чем версия '85 (Back-to-the-Future).

4 голосов
/ 27 мая 2011

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

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

РЕДАКТИРОВАТЬ: Кроме того, этот файл является pre-K & RC, что довольно круто, но есть некоторые существенные отличия от современного C, вам не обязательно подражать ему.

2 голосов
/ 27 мая 2011

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

Серьезно, найдите себе более новый код, чем какой-то старый стиль C & R в стиле oldskool, с тех пор язык C значительно эволюционировал, в процессе вы можете усвоить пару вредных привычек.

2 голосов
/ 27 мая 2011

Код будет концептуально читаться как хорошая спецификация

  • Сначала определите интерфейс, используемый основной точкой входа
  • Затем определите основную точку входа
    • Тогда поведение программы в идеальном случае концептуально полностью определено.
  • Наконец, реализуйте интерфейс (определения после main).

То, что он опускает объявления функций, которые должны быть помещены перед main (чтобы удовлетворить первый пункт - определение интерфейса), многими программистами считается плохим стилем относительно современного C.

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

1 голос
/ 27 мая 2011

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

/* this is a definition and a prototype */
int fx1(void) {
    return 42;
}

/* this is only a prototype */
int fx2(int, const char*);

int main(void) {
    fx1(); /* ok, prototype in scope */
    fx2(42, "foobar"); /* ok, prototype in scope */
}

/* fx2 definition.
** it is undefined behaviour if the prototype here
** does not match the previous prototype */
int fx2(int k, const char *t) {
    return strlen(t) - k;
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...