Включити один вихідний файл C в інший?


111

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


4
Я відважився відредагувати питання, а також застосував кілька зручних тегів. Деякі мої теги можуть виявити упередженість, хе.
розмотувати

Відповіді:


113

Якщо правильно використовувати, це може бути корисною технікою.

Скажімо, у вас складна, критична підсистема з досить невеликим загальнодоступним інтерфейсом і безліччю коду реалізації, який не використовується повторно. Код працює на кілька тисяч рядків, сто або більше приватних функцій і зовсім небагато приватних даних. Якщо ви працюєте з нетривіальними вбудованими системами, ви, ймовірно, стикаєтеся з цією ситуацією досить часто.

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

З C ви можете багато чого втратити, зробивши це. Практично всі ланцюги інструментів забезпечують гідну оптимізацію для однієї компіляційної одиниці, але дуже песимістично ставляться до всього, що оголошено зовнішнім.

Якщо ви помістите все в один вихідний модуль C, ви отримаєте -

  • Поліпшення продуктивності та розміру коду - виклики функцій будуть вказуватися у багатьох випадках. Навіть не вкладаючи, компілятор має можливість створити більш ефективний код.

  • Приховування даних рівня функцій та функцій.

  • Уникнення забруднення простору імен та його наслідків - ви можете використовувати менш громіздкі імена.

  • Швидше складання та зв’язок.

Але ви також отримуєте нечестивий безлад, коли справа доходить до редагування цього файлу, і ви втрачаєте припущену модульність. Це можна подолати, розділивши джерело на кілька файлів і включивши їх для отримання єдиного блоку компіляції.

Для правильного управління цим вам потрібно ввести деякі умови. Вони певною мірою залежатимуть від вашої ланцюжка інструментів, але деякі загальні вказівки -

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

  • Майте один головний .c файл, який включає всі допоміжні файли .c. Сюди також можна включити код для загальнодоступного інтерфейсу.

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

  • Усі приватні дані та функції повинні бути оголошені статичними.

  • Підтримуйте концептуальне розмежування файлів .c та .h. Це використовує існуючі конвенції. Різниця полягає в тому, що у вас буде багато статичних декларацій у своїх заголовках.

  • Якщо ваша інструментальна мережа не нав'язує жодних причин цього не робити, називайте приватні файли реалізації як .c та .h. Якщо ви використовуєте включені охоронці, вони не створюватимуть код і не вводять нових імен (ви можете виявити деякі порожні сегменти під час посилання). Величезна перевага полягає в тому, що інші інструменти (наприклад, IDE) будуть належним чином обробляти ці файли.


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

Я бачив, як багато програм це роблять, і це стає мені на нерви, коли я намагаюся повторно використовувати їх код. Дякуємо, що пояснили, чому вони це роблять, і запропонували використовувати охорону (що часто не робиться).
Джої Адамс

У вбудованій розробці для C51 використання зовнішніх та кількох файлів 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, так і багато ручних Makefiles), які розраховують на компіляцію * .c - якщо це станеться, ви, ймовірно, отримаєте помилки в посиланнях через дублюючі символи.

Як правило, цієї практики слід уникати.

Якщо ви абсолютно повинні #include джерело (і його взагалі слід уникати), використовуйте інший файловий суфікс для цього файлу.


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, вам слід оголосити її в основному також, використовуючи зовнішній клас зберігання.


5

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

Однак, залежно від параметрів makefile чи проекту, включений файл c може генерувати окремий файл об'єкта. При посиланні це може призвести до подвійно визначених символів.


3

Ви можете правильно включити .C або .CPP файли в інші вихідні файли. Залежно від вашої IDE, ви зазвичай можете запобігти подвійному зв’язку, переглянувши властивості вихідних файлів, які ви хочете включити, як правило, клацнувши правою кнопкою миші та клацнувши по властивостях, і зніміть прапорці / перевірити компіляцію / посилання / виключити з збірки або будь-який інший варіант може бути. Або ви не можете включити файл у сам проект, таким чином IDE навіть не знає, що він існує, і не намагається його скомпілювати. І за допомогою файлів ви просто просто не вкладаєте файл у нього для компіляції та зв’язування.

EDIT: Вибачте, що я зробив відповідь замість відповіді на інші відповіді :(


1

Мова C не забороняє такого роду #include, але отримана одиниця перекладу все ж має бути дійсною C.

Я не знаю, яку програму ви використовуєте з файлом .prj. Якщо ви використовуєте щось на кшталт "make" або Visual Studio чи будь-що інше, просто переконайтеся, що ви встановили його список файлів для компіляції без того, який неможливо скласти самостійно.


0

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

До речі, я пропустив другу частину питання. Якщо файл C включений в інший файл і в той же час включений в проект, ви, мабуть, зіткнетеся з проблемою дублювання символів, чому зв'язування об'єктів, тобто одна і та ж функція буде визначена двічі (якщо всі вони статичні).


-6

ви повинні додати такий заголовок

#include <another.c>

Примітка: обидва файли повинні бути розміщені в одному місці

Я використовую це в кодевізорі AVR для мікроконтролерів ATMEGA і працюю по-справжньому, але це не працює в нормальному мовному файлі C

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.