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

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

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

unwind 10.07.2009 16:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
122
1
169 379
11

Ответы 11

Нет.

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

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

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

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

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

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

Также стоит отметить, что даже если он компилируется, он может не связываться, если файл #included .c также скомпилирован, а два объектных файла связаны вместе - вы можете получить несколько определенных символов.

Nick Meyer 10.07.2009 16:50

Небольшой вопрос. У меня есть файл заголовка, объявляющий метод struct +, а также соответствующий файл .c для их определения. Если этот метод принимает структуру в качестве параметра, как мне избежать включения файла .c в другой файл .c, где определен основной метод?

stdout 09.03.2019 16:23

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+1 это все еще реальность, а лучшие компиляторы сделают этот метод устаревшим со временем. GCC 4.5 с оптимизацией времени компоновки - большой шаг на этом пути.

u0b34a0f6ae 10.07.2010 13:59

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

Joey Adams 19.07.2011 12:15

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

mahesh 24.02.2017 00:23

Для записей: GCC поддерживает оптимизацию для разных единиц перевода, см. этот SO поток и раздел руководства GCC на оптимизация времени ссылки.

Twonky 05.05.2018 18:51

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

mtraceur 01.03.2021 06:24

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

mtraceur 01.03.2021 06:30

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

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

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

Обновлено: Извините, я сделал это ответом вместо ответа на другие ответы :(

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

#include <another.c>

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

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

Вы можете использовать компилятор 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.

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

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

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

/**
 * @file   vendor_wrap.c
 * @brief  vendor source code wrapper to prevent warnings
 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnested-externs"
#include "vendor_source_code.c"
#pragma GCC diagnostic pop

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

gcc main.c vendor_wrap.c -o $(CFLAGS) main.out

Другие вопросы по теме