2010-09-24 4 views
226

Я работаю над проектом, который имеет много наследия Код C. Мы начали писать на C++, имея в виду, наконец, преобразовать устаревший код. Я немного смущен тем, как взаимодействуют C и C++. Я понимаю, что, обернув код C с помощью extern "C", компилятор C++ не будет искажать имена кодов C, но я не совсем уверен, как это реализовать.Объединение C++ и C - как работает #ifdef __cplusplus?

Таким образом, в верхней части каждого файла заголовка C (после того, как включить охрану), мы имеем

#ifdef __cplusplus 
extern "C" { 
#endif 

и на дне, мы пишем

#ifdef __cplusplus 
} 
#endif 

между двумя , у нас есть все наши включения, typedefs и прототипы функций. У меня есть несколько вопросов, чтобы увидеть, если я понять это правильно:

  1. Если у меня есть C++ файл A.hh, который включает в себя C файл с заголовка Bh, включает другой заголовок C файл Ch, Как это работает? Я думаю, что когда шаги компилятора в B.h, будут определяться __cplusplus, так это будет обернуть код с extern "C"__cplusplus не будут определены внутри этого блока). Итак, , когда он вступает в C.h, __cplusplus не будет определен , и код не будет обернут в extern "C". Это верно?

  2. Есть ли что-то не так с обертыванием фрагмента кода с extern "C" { extern "C" { .. } }? Что будет делать второй extern "C" ?

  3. Мы не помещаем эту оболочку вокруг файлов .c, только файлы .h. Итак, что произойдет, если функция не имеет прототипа? Компилятор считает, что это C++-функция?

  4. Мы также используем некоторые сторонние код, написанный на C , и не не имеют такого рода обертки вокруг это. Каждый раз, когда я включаю заголовок из этой библиотеки, я помещал extern "C" вокруг #include. Это правильный способ справиться с что?

  5. И, наконец, это хорошая идея? Есть ли что-нибудь еще, что мы должны делать? Мы собираемся смешать C и C++ в обозримом будущем, а I хочу удостовериться, что мы покрываем все базы .

+6

Посмотрите на [C++ FQA: смешивание C и C++] (http://yosefk.com/c++fqa/mixing.html) и [C++ FAQ: смешение C и C++] (http: // www .parashift.com/C++ - чаво-облегченный/смесительно-с-и-cpp.html). – Lazer

+0

Вкратце, это лучшее объяснение: 'Чтобы гарантировать, что имена, объявленные в этой части кода, имеют C-ссылку, и, таким образом, сбой имени C++ не выполняется.' (у меня есть это [ссылка] (https : //cboard.cprogramming.com/c-programming/150020-whats-purpose-sharpifdef-__cplusplus.html)) – anhldbk

ответ

207

extern "C" не меняет способ, которым компилятор читает код. Если ваш код находится в .c файле, он будет скомпилирован как C, если он находится в файле .cpp, он будет скомпилирован как C++ (если вы не сделаете что-то странное для своей конфигурации).

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

Код внутри extern "C" по-прежнему является кодом на C++. Есть ограничения на то, что вы можете сделать во внешнем блоке «C», но все они связаны с привязкой. Вы не можете определить какие-либо новые символы, которые нельзя построить с помощью C-ссылки. Это означает, что нет классов или шаблонов, например.

extern "C" блокирует гнездо красиво. Также есть extern "C++", если вы оказались безнадежно запертыми внутри областей extern "C", но это не такая хорошая идея с точки зрения чистоты.

Теперь, особенно в отношении ваших пронумерованных вопросов:

Что касается № 1: __cplusplus должен быть определен внутри extern "C" блоков. Это не имеет значения, так как блоки должны аккуратно гнездиться.

Относительно # 2: __cplusplus будет определен для любого блока компиляции, который выполняется через компилятор C++. Как правило, это означает, что файлы .cpp и любые файлы включены в этот файл .cpp. То же самое .h (или .hh или .hpp или what-have-you) можно интерпретировать как C или C++ в разное время, если к ним относятся разные единицы компиляции. Если вы хотите, чтобы прототипы в файле .h ссылались на имена символов C, они должны иметь extern "C", когда они интерпретируются как C++, и они не должны иметь extern "C" при интерпретации C - следовательно, проверка #ifdef __cplusplus.

Чтобы ответить на ваш вопрос # 3: функции без прототипов будут иметь C++-связь, если они находятся в .cpp-файлах, а не внутри блока extern "C". Это прекрасно, хотя, потому что, если у него нет прототипа, его можно вызвать только другими функциями в том же файле, а затем вам вообще не нравится, как выглядит ссылка, потому что вы не планируете использовать эту функцию все равно вызывать что-либо за пределами того же модуля компиляции.

Для # 4 у вас есть это точно. Если вы включаете заголовок для кода, который имеет C-ссылку (например, код, который был скомпилирован компилятором C), тогда вы должны extern "C" заголовок - таким образом вы сможете связаться с библиотекой. (В противном случае, ваш компоновщик будет искать функции с такими именами, как _Z1hic, когда вы искали void h(int, char)

5: Этот вид смешения является общей причиной для использования extern "C", и я не вижу ничего плохого делать это это путь. - просто убедитесь, что вы понимаете, что вы делаете

+3

Хорошо упомянуть 'extern 'C++" ', когда ваш заголовок/код C++ находится глубоко внутри некоторого кода C – deddebme

+1

Я написал простую программу на C. Внутри я добавил блок #ifdef __cplusplus и добавил printf («__ cplusplus defined \ n»); в этом. Если я скомпилирую его с помощью gcc, «__cplusplus defined» не печатается, но если я скомпилирую его с g ++, он будет напечатан. Поэтому я считаю, что __cplusplus означает, что компилятор - это компилятор C++ (вы сказали это). Разве это не правильно? (потому что я видел, что вы говорите: «__cplusplus должен быть определен внутри внешних блоков C». Можем ли мы определить __cplusplus явно? –

+0

Хотя вы должны иметь возможность определить (почти) все, что хотите, вся точка '__cplusplus' должна определить, используется ли 'C++' против 'C', поэтому определение его вручную/явно не соответствует его цели ... – nurchi

31
  1. extern "C" не меняет наличие или отсутствие __cplusplus макро. Он просто изменяет привязку и обработку имен завернутых объявлений.

  2. Вы можете гнездиться extern "C" блоков довольно счастливо.

  3. Если вы компилируете .c файлы как C++, то ничего не в extern "C" блоке, и без него extern "C" прототип будет рассматриваться как функция C++. Если вы скомпилируете их как C, то, конечно, все будет функцией C.

  4. Да

  5. Вы можете смело смешивать C и C++ таким образом.

15

несколько подводных камней, которые colloraries превосходному ответ Эндрю Shelansky и на согласные немного с не меняет так, что компилятор считывает код

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

Ошибки компилятора будут, если вы попытаетесь использовать возможности прототипа C++, такие как перегрузка.

Компоновщик ошибка будет происходить позже, потому что ваша функция будет отображаться не будет найден, если вы не обратитесь в ехЬегп «C» обертка деклараций и заголовок включаются в смеси источника C и C++ ,

Одна из причин, по которой люди не могут использовать команду , компилировать C как C++, потому что это означает, что их исходный код больше не переносится. Этот параметр является настройкой проекта, поэтому, если файл .c удаляется в другой проект, он не будет скомпилирован как C++. Я бы предпочел, чтобы люди потратили время на переименование суффиксов файлов на .cpp.

+1

Это была загадочная причина, вытаскивающая мои волосы. –