Включать один исходный файл C в другой?

112

Это нормально (или даже рекомендуется / хорошая практика) в #includeвиде .cфайла в другой .cфайл?

СС Энн
источник
4
Я взял на себя смелость отредактировать вопрос, а также применил несколько удобных тегов. Некоторые из моих тегов могут показывать предвзятость, хех.
расслабьтесь

Ответы:

114

При правильном использовании это может оказаться полезным методом.

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

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

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

Если вы поместите все в один исходный модуль C, вы получите -

  • Улучшения производительности и размера кода - во многих случаях вызовы функций будут встроены. Даже без встраивания у компилятора есть возможности для создания более эффективного кода.

  • Скрытие данных и функций на уровне ссылок.

  • Избежание загрязнения пространства имен и его следствия - вы можете использовать менее громоздкие имена.

  • Более быстрая компиляция и компоновка.

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

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

  • Поместите публичный интерфейс в отдельный файл заголовка - вы все равно должны это делать.

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

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

  • Все частные данные и функции должны быть объявлены статическими.

  • Сохраняйте концептуальное различие между файлами .c и .h. Это усиливает существующие соглашения. Разница в том, что в ваших заголовках будет много статических объявлений.

  • Если в вашей инструментальной цепочке нет причин не делать этого, назовите частные файлы реализации как .c и .h. Если вы используете include guards, они не будут производить код и не вводить новые имена (вы можете получить некоторые пустые сегменты во время связывания). Огромным преимуществом является то, что другие инструменты (например, IDE) будут обрабатывать эти файлы соответствующим образом.


источник
1
+1 это все еще реальность, а лучшие компиляторы сделают этот метод устаревшим со временем. GCC 4.5 с оптимизацией времени компоновки - большой шаг на этом пути.
u0b34a0f6ae
Я видел так много программ, и это действует мне на нервы, когда я пытаюсь повторно использовать их код. Спасибо, что объяснили, почему они это делают, и предложили использовать охрану (что часто не делается).
Joey Adams
Во встроенной разработке для C51 использование extern и нескольких файлов C не вызвало ничего, кроме головной боли. Я перехожу на один файл C, включающий все остальные, чтобы вернуть свое время.
mahesh
Для записей: GCC поддерживает оптимизацию для разных единиц перевода, см. Этот поток SO и раздел руководства GCC по оптимизации времени компоновки .
Twonky
60

Это нормально? да, он будет компилироваться

это рекомендуется? no - файлы .c компилируются в файлы .obj, которые после компиляции (компоновщиком) связываются вместе в исполняемый файл (или библиотеку), поэтому нет необходимости включать один файл .c в другой. Вместо этого вы, вероятно, захотите создать файл .h, в котором перечислены функции / переменные, доступные в другом файле .c, и включить файл .h

Стивен А. Лоу
источник
14
Также стоит отметить, что даже если он компилируется, он может не связываться, если файл #included .c также скомпилирован и два объектных файла связаны друг с другом - вы можете получить несколько определенных символов.
Ник Мейер,
1
Небольшой вопрос. У меня есть файл заголовка, объявляющий метод struct +, а также соответствующий файл .c для их определения. Если этот метод принимает структуру в качестве параметра, как мне избежать включения файла .c в другой файл .c, где определен основной метод?
stdout
12

Нет.

В зависимости от вашей среды сборки (вы не указываете), вы можете обнаружить, что она работает именно так, как вы хотите.

Однако существует множество сред (как IDE, так и множество созданных вручную Makefile), которые ожидают компиляции * .c - если это произойдет, вы, вероятно, получите ошибки компоновщика из-за повторяющихся символов.

Как правило, такой практики следует избегать.

Если вам абсолютно необходимо #include source (и обычно этого следует избегать), используйте для файла другой суффикс.

Эндрю Эджкомб
источник
9

Я подумал, что поделюсь ситуацией, когда моя команда решила включить файлы .c. Наш архив в основном состоит из модулей, разделенных системой сообщений. Эти обработчики сообщений являются общедоступными и вызывают многие локальные статические рабочие функции для выполнения своей работы. Проблема возникла при попытке охватить наши примеры модульного тестирования, поскольку единственный способ реализовать этот частный код реализации - косвенно через интерфейс общедоступных сообщений. С некоторыми рабочими функциями по колено в стеке, это оказалось кошмаром для достижения надлежащего покрытия.

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

Мэтью Алфорд
источник
6

Вы можете использовать компилятор gcc в Linux, чтобы связать два файла c в один вывод. Предположим, у вас есть два файла c: один - main.c, а другой - support.c. Итак, команда для связывания этих двух

gcc main.c support.c -o main.out

Таким образом, два файла будут связаны с одним выходом main.out Для запуска вывода команда будет

./main.out

Если вы используете функцию в main.c, которая объявлена ​​в файле support.c, вам следует объявить ее в main, также используя класс хранения extern.

Сумитрой
источник
5

Расширение файла не имеет значения для большинства компиляторов C, поэтому оно будет работать.

Однако, в зависимости от вашего make-файла или настроек проекта, включенный c-файл может создать отдельный объектный файл. При связывании это может привести к двойному определению символов.

HS.
источник
3

Вы можете правильно включать файлы .C или .CPP в другие исходные файлы. В зависимости от вашей IDE вы обычно можете предотвратить двойную привязку, просмотрев свойства исходных файлов, которые вы хотите включить, обычно щелкнув правой кнопкой мыши по нему и щелкнув свойства, а также снимите / отметьте компиляцию / ссылку / исключить из сборки или любой другой вариант, который он может быть. Или вы не можете включить файл в сам проект, поэтому IDE даже не узнает о его существовании и не попытается его скомпилировать. А с make-файлами вы просто не поместите файл в него для компиляции и компоновки.

РЕДАКТИРОВАТЬ: Извините, я сделал это ответом вместо ответа на другие ответы :(

MikeyPro
источник
1

Язык C не запрещает такие #include, но полученная единица перевода все равно должна быть действительной C.

Я не знаю, какую программу вы используете с файлом .prj. Если вы используете что-то вроде «make», Visual Studio или что-то еще, просто убедитесь, что вы установили его список файлов для компиляции без того, который не может компилироваться независимо.

Программист Windows
источник
0

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

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

Илья
источник
-6

вы должны добавить такой заголовок

#include <another.c>

примечание: оба файла должны находиться в одном месте

Я использую это в codevison AVR для микроконтроллеров ATMEGA и работаю правильно, но не работает в обычном файле языка C

Milad
источник