Требуется также добавить "extern C" в исходный файл?

Недавно я нашел код, в котором extern "C" был добавлен в исходный файл также для функций. Они также были добавлены в файлы заголовков, где они были объявлены.

Я был в предположении, что добавления "extern" C "в заголовочных файлах было достаточно.

Где следует добавлять внешние блоки "C" ?

UPDATE: Предположим, что я компилирую свой C-код с помощью CPP-компилятора и добавили внешние защитники "C" для всех функций в файлах заголовков (т.е. Все мои функции имеют свои прототипы в заголовках), но в исходных файлах я не добавил их. Это вызовет проблему?

Ответы

Ответ 1

Поскольку вы имеете в виду

extern "C" { ... }

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

Цель состоит в том, чтобы позволить С++-коду взаимодействовать с кодом C, который обычно находится в библиотеке. Если заголовки библиотеки не были написаны с учетом С++, то они не будут включать в себя защитные элементы extern "C" для С++.

Заголовок C, написанный с учетом С++, будет включать в себя что-то вдоль строк

#ifdef __cplusplus
extern "C" {
#endif

...

#ifdef __cplusplus
}
#endif

чтобы убедиться, что программы на С++ видят правильную связь. Однако не все библиотеки были написаны с учетом С++, поэтому иногда вам приходится делать

extern "C" {
#include "myclibrary.h"
}

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

extern "C" не является (AFAIK) ANSI C, поэтому не может быть включен в обычный код C без предохранения от препроцессора.

В ответ на ваше редактирование:

Если вы используете компилятор С++ и объявляете функцию extern "C" в файле заголовка, вам не нужно также объявлять эту функцию как extern "C" в файле реализации. Из раздела 7.5 стандарта С++ (акцент мой):

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

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

Ответ 2

Они должны входить только во все, что включено в другие исходные файлы.

С некоторыми идиомами вы найдете людей, включая исходные файлы.

Ответ 3

Они должны быть добавлены ко всем файлам, которые входят в другие файлы.

Обычно не включаются исходные файлы.

Ответ 4

Вы имеете в виду препроцессоры "extern c"? Они должны быть в определении функции, а также влияют на то, как вызов функции хранится в скомпилированном двоичном файле. Это действительно необходимо, если вы связываете скомпилированный С++ вместе с c, который скомпилирован как C (в отличие от c в файле .cpp).

Ответ 5

Апология

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


Оригинальный ответ

Нет, нет необходимости включать защитные устройства в код C.

Если заголовочный файл 'header.h' говорит:

#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
...
#endif

Тогда для исходного файла 'source.c' совершенно безопасно сказать:

#include "header.h"

В других заголовках также безопасно включать 'header.h'.

Однако люди отмечают, что открытие файла заголовка и чтение его требуют времени, что замедляет компиляцию, поэтому иногда люди делают такие вещи, как:

#ifndef HEADER_H_INCLUDED
#include "header.h"
#endif

Это означает, что если какой-либо другой заголовок, включенный в 'source.c', уже включил 'header.h', тогда '#include' не обрабатывается повторно. (Или, если 'header.h' уже был включен непосредственно в 'source.c', хотя это глупый buglet.)

Таким образом, при обнаружении, вероятно, это будет попытка оптимизировать производительность компиляции. Далеко не ясно, что он много покупает; современные препроцессоры C довольно разумны в этой проблеме и избегают повторного включения файла, если могут. И всегда существует риск того, что тест в "source.c" имеет опечатку (возможно, #ifndef HEARER_H_INCLUDED), и в этом случае тест замедляет компиляцию, потому что препроцессор проверяет несоответствующее условие, а затем переходит к включению "header.h" после все. Это безопасно'; заголовок сам защищен - или должен быть.

Если вы видите, что код в 'source.c' также выполняет '#define HEADER_H_INCLUDED', тогда возникают проблемы. #define должен быть либо до, либо после #include, и ни один из них не является хорошим как общий метод.

  • Если 'source.c' делает '#define HEADER_H_INCLUDED' перед включением 'header.h', то если защита появляется в 'header.h', содержимое заголовка не будет включено. Если защитник не отображается в 'header.h', тогда все работает нормально.
  • Если 'source.c' делает '#define HEADER_H_INCLUDED' после включения 'header.h', тогда, если защита появляется в 'header.h', мы получаем доброкачественное переопределение HEADER_H_INCLUDED. Если "header.h" не содержит защитника, но включает файл, который включает "header.h" , вы не защищены от множественного включения в конце концов.

Обратите внимание, что тело заголовка появляется после '#define HEADER_H_INCLUDED'. Это снова защита, если вложенные включают include 'header.h'.

Ответ 6

Защитники "С" имеют две цели:

  • Когда ваш код скомпилирован, функции будут экспортироваться таким образом, чтобы использовать компилятор/компоновщик не С++ для использования (без изменения имени С++ и т.д.).
  • Когда компилятор С++ использует ваши файлы заголовков, он будет знать, что он должен связывать символы на языке C, что, в свою очередь, гарантирует, что результирующая программа будет успешно связываться. Они не имеют значения для компилятора, отличного от С++, но поскольку символы были сгенерированы в C-стиле в (1), это желаемый эффект.

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

Ответ 7

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

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

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

В общем случае следует избегать использования определения extern. Они легко приводят к неуправляемому коду и ошибкам, которые трудно найти. Конечно, есть примеры, когда другие решения были бы непрактичными, но они редки. Например, stdin и stdout являются макросами, которые сопоставляются с переменной внешнего элемента типа FILE * в stdin.h; пространство памяти для этого массива находится в стандартном блоке C-библиотеки.