Определить функцию перед основной? - PullRequest
9 голосов
/ 04 ноября 2010

Обязательны ли объявления / прототипы функций в C99?

В настоящее время я определяю свои функции в заголовочном файле и # include-ING - в основном файле. Это нормально в C99?

Почему большинство программистов объявляют / прототипируют функцию перед main () и определяют ее после main ()? Не проще ли определить их перед main и избежать всех объявлений / прототипов?

Содержимое файла header.h:

int foo(int foo)
{
// code
return 1;
}

Содержимое основного файла:

#include <stdio.h>

#include "header.h"

int main(void)
{
foo(1);
return 0;
}

Ответы [ 10 ]

14 голосов
/ 04 ноября 2010

Как и где создать прототип и определить функцию в C:

  1. Ваша функция используется только в конкретном файле .c: Определите это статически в .c файле. Функция будет видна и скомпилирована только для этого файла.

  2. Ваша функция используется в нескольких файлах .c: Выберите соответствующий файл c для размещения вашего определения (например, все функции, связанные с foo в файле foo.c), и имейте связанный файл заголовка для создания прототипа всех нестатических (думаю, публичных) функций. Функция будет скомпилирована только один раз, но видима для любого файла, содержащего файлы заголовков. Все будет собрано во время ссылки. Возможное улучшение: всегда делайте связанный заголовочный файл, первый включенный в его c-файл, таким образом, вы будете уверены, что любой файл может включать его безопасно, без необходимости других включений, чтобы заставить его работать, ссылка: Large Масштаб проектов C ++ (большинство правил применимо и к C).

  3. Ваша функция недоступна (вы уверены, что это так?): Определите функцию static inline в соответствующем заголовочном файле. Компилятор должен заменить любой вызов вашей функции определением, если это возможно (подумайте, как в макросе).

Понятие до-после другой функции (вашей основной функции) в c - это вопрос стиля. Либо вы делаете:

static int foo(int foo) 
{ 
// code 
return 1; 
} 

int main(void) 
{ 
foo(1); 
return 0; 
} 

Или

static int foo(int foo);

int main(void) 
{ 
foo(1); 
return 0; 
} 

static int foo(int foo)
{ 
// code 
return 1; 
} 

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

5 голосов
/ 04 ноября 2010

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

4 голосов
/ 04 ноября 2010

Функция декларации требуются в C99. Функция прототипы не требуются в C99.

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

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

4 голосов
/ 04 ноября 2010

Вы должны когда-либо определять inline функции только в заголовках. Хотя у вас может быть extern inline функций, общий случай static inline.

Практическое правило для заголовочных файлов:

  • объявления функций должны быть extern
  • определения функции должны быть static inline
  • объявления переменных должны быть extern
  • определения переменных должны быть static const

Как и просил К. Росс, здесь есть причины: ресурс с внешней связью должен быть определен только один раз [1]. Из этого следует, что определения не должны находиться в заголовочных файлах, которые предназначены для включения в более чем одно место.

Наличие static определений в заголовочных файлах не приведет к каким-либо проблемам, но обычно вызывает неодобрение, поскольку код должен быть скомпилирован более одного раза и будет присутствовать в разных объектных файлах, что увеличит размер исполняемого файла при условии, что компоновщик недостаточно умен, чтобы выяснить дублирование кода).

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

Примечание: [1] Это не относится к inline функциям с внешним связыванием, но так как не указано, какое из множества определений встроенной функции будет использоваться при оценке указателя функции они в основном бесполезны

3 голосов
/ 04 ноября 2010

Ваш подход подходит для небольших программ. Заголовочные файлы предназначены для объявлений и постоянных определений - они предоставляют интерфейс для программы, которую они «инкапсулируют». Заголовки предназначены в качестве интерфейса для других программных модулей.

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

1 голос
/ 04 ноября 2010

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

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

1 голос
/ 04 ноября 2010

Да, их легче определить перед главными. Если вы хотите использовать эти функции только внутри файла, прототип не нужен. Однако в этом случае вы также можете добавить ключевое слово «static» перед определением функции. (В файле C.) Это гарантирует, что функция не видна другим файлам. (Во время ссылки.)

Не помещайте статические ключевые слова во включаемые файлы.

0 голосов
/ 19 февраля 2018

Рекомендуется объявлять функции либо в главном, либо в отдельном заголовочном файле, который будет включен в другие файлы c, где мы использовали эту функцию.Делая это, мы можем легко идентифицировать все функции, объявленные / определенные в этих файлах .C или .H.И мы должны использовать ключевое слово extern перед объявлением функции в заголовочном файле.

0 голосов
/ 04 ноября 2010

Почему большинство программистов объявляют / прототипируют функцию перед main () и определяют ее после main ()?

Просто потому, что большинство людей читают последовательно. Начните историю с начала, а не с середины. Не обязательно, просто интуитивно понятно.

Конечно, если прототип кода находится в отдельном модуле компиляции, прототипы необходимы.

0 голосов
/ 04 ноября 2010

Вы всегда должны быть прототипом.

Причины этого:

  1. методическое прототипирование создает краткий список в заголовочных файлах функций в коде - это неоценимо для будущих читателей

  2. во всех проектах, кроме простейших, многие функции не будут видны до main.

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

...