Компиляторы по-разному обрабатывают файлы .h или «просто» соглашение об именах? - PullRequest
4 голосов
/ 06 мая 2019

Я новичок в программировании и компиляции семейства C.Я пытаюсь понять, что файлы .c / .cpp против файлов .h находятся на низком техническом уровне.Я понимаю, что файлы .h используются для указания интерфейса, а файлы .c или .cpp для реализации.Однако мне интересно, отражается ли это различие в том, как работает компилятор, или это просто соглашение об именах, упрощающее для нас, людей?Не могли бы вы теоретически иметь реализацию в файле .h и все же иметь возможность ее компилировать?Или указать интерфейс в .c файле?Я прошу лучше понять, что на самом деле делает компилятор.

Ответы [ 5 ]

9 голосов
/ 06 мая 2019

Технически нет никакой разницы между расширениями в том, что касается компилятора.Это так же, как если бы вы ввели вручную содержимое любого файла в том месте, куда вы положили #include.Вы можете набрать #include "foo.pdf", и компилятор успешно включит файл foo.pdf, если этот файл содержит код (несмотря на расширение)

По соглашению теперь обычно помещают объявления в файлы .h / hpp(или определения шаблонов) и реализация в файлах .c / cpp.

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

Иногда также включаемый файл даже не существует, например, при включении стандартного файла STL (например, string), компилятор может вообще не читать файл, но кэшировать его / реализовать его так, как ему хочется.

Подробнее о #include в MSDN и CPPReference .

5 голосов
/ 06 мая 2019

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

Если вы укажетекомпилятор для компиляции файла .h (и передачи параметров командной строки, чтобы он рассматривал его как исходный файл, например -x c для gcc), он скомпилирует его просто отлично.Если вы #include "a.cpp", он будет включен нормально.

2 голосов
/ 06 мая 2019

В основном это просто соглашение об именах, которое помогает нам организовать наш код; нет никаких технических различий на низком уровне, по крайней мере для пользовательских заголовков (некоторые реализации могут иметь «предварительно скомпилированные» заголовки, чтобы не было читаемого исходного текста). Все это должно быть допустимым кодом C или C ++. Стандарт языка C называет все заголовки стандартной библиотеки с расширением .h, поэтому большинство людей следуют соглашению в отношении заголовков. Стандарт языка C ++ называет все заголовки стандартной библиотеки с расширением no (iostream, string и т. Д.), Но большинство людей придерживаются соглашения об именовании .h (или .hpp), главным образом сделать поиск проще.

Отдельные инструменты могут заботиться - gcc будет обрабатывать файл .c иначе, чем файл .cpp, в среде IDE файлы .h могут отображаться не так, как файлы .c - но это функция конкретного инструмента, а не язык.

Некоторые (по общему признанию) системы использовали соглашение об именовании файлов, которое вообще не допускало расширения .h или .c - MPE на HP3000 использовал соглашение <em>filename.groupname.accountname</em>. Компилятор C в MPE смог правильно отобразить стандартные имена заголовков, такие как stdio.h и stdlib.h, но пользовательские заголовки имели , чтобы следовать формату <em>filename.groupname.accountname</em> (все из которых должны были соответствовать 35 символов или менее, включая разделители, приводящие к чудесно читаемым именам, таким как MYCODEHDR.DEVELOP.BODE).

1 голос
/ 06 мая 2019

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

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

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

0 голосов
/ 06 мая 2019

В случае заголовочных файлов это просто соглашение.

Вы также можете включить другой код, старый шаблон которого был:

file.inc:

  MACRO("-a", "--append", "this will append text"),
  MACRO("-b", "--bottom", "something for bottom"),

и в основном файле:

char *options[] = {
#define MACRO(short, long, help) (short)
#include "file.inc"
#undef MACRO
NULL
};

char *help[] = {
#define MACRO(short, long, help) (help)
#include "file.inc"
#undef MACRO
NULL
};

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

Ядро Linux иногда включает в себя другие * .c файлы,например, изменение нескольких функций с помощью макроса.Я не думаю, что обычно это хороший стиль кодирования, но ядро ​​использует его для сборки, например, для драйверов, которые совместно используют 99,9% кода.

Примечание: #include <include.h> отличается.В этом случае include.h может быть интерпретирован как метка для компилятора (который может использовать его как флаг).В системе нет необходимости иметь заголовки стандартных библиотек, но современные распространенные компиляторы имеют заголовки также для стандартных библиотек.

Файл h может быть пустым или содержать окончательный перевод строки \n.Других требований нет.Для контекста это должно быть допустимым C.

Исторически существовали препроцессор cpp и компилятор cc как две разные программы.Таким образом, препроцессор ничего не знает о соглашении и структуре имени файла.Компилятор, чем компилировать все остальное, просто как один файл.См. Выше о возможном исключении для стандартных библиотек.Примечание: я видел также использование препроцессора C в сценариях оболочки, но не в файлах C.(gcc -E

Последний пункт. Некоторые компиляторы, например, gcc, используют расширение файла для выбора используемого языка. Вы можете перезаписать его с помощью параметров командной строки. Но так, gcc a.h не может скомпилировать файлтак, как вы ожидаете. Например, в моей системе touch a.h b.c; gcc a.h; gcc b.c дает два разных результата.

...