Включенные файлы, все или ничего?

Я писал код некоторое время, но я не классически обучен в области информатики, поэтому, если этот вопрос нелепо, пожалуйста, пройдите на меня.

Что-то, на что я пытаюсь найти окончательный ответ, какое-то время, если я # включаю файл на C, получаю ли содержимое ENTIRE связанного файла или только те части, которые я использую? Если в нем есть 10 функций, и я использую только одну из функций, входит ли код для остальных 9 функций в мой исполняемый файл? Это особенно актуально для меня сейчас, когда я работаю над микроконтроллером, и память драгоценна.

Спасибо за любую помощь в этом вопросе.

Ответы

Ответ 1

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

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

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

В-четвертых, если ваш файл заголовка имеет какой-то необычный вид, который содержит определения функций (или объектов), то см. "во-первых" и "во-вторых" выше. Собственный компилятор может видеть только одну единицу трансляции за раз, поэтому типичной стратегией для самого компилятора является полное отбрасывание неиспользуемых объектов с внутренней связью (т.е. static объектов и функций) и сохранение всех объектов с внешней связью. Объекты с внешней связью не могут быть отброшены собственно компилятором, так как они могут понадобиться в какой-либо другой единицы перевода.

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

Ответ 2

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

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

Итак, чтобы ответить на ваш вопрос: только ваши функции (и косвенно зависят) будут включены в ваш окончательный файл программы, и это не зависит от того, какие файлы вы #include. Счастливый взлом!

Ответ 3

Вы должны различать разные сценарии:

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

Относительно точки 1: Только внешние выражения #include ing, никакой другой код не станет частью вашего объектного файла. И определения статических функций, которые являются частью файла заголовка, но не ссылаются на ваш код, могут не стать частью вашего объектного файла - это довольно простая оптимизация. Однако это зависит от вашего компилятора.

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

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

Ответ 4

Include просто представляет компилятор в конечном итоге с тем, что похоже на один файл (и если вы сохраните-temps на gcc, вы увидите, что точный одиночный файл представлен фактическому компилятору). Это не сложнее. поэтому, если у вас есть прототипы функций или их определения в вашем .c файле, то наличие их из include не имеет никакого значения, конечный результат тот же.

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

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

все эти вещи вы могли бы разобраться с некоторыми очень маленькими примерами и примерно на 3 - 5 минут. Я предлагаю вам пойти на это.