Когда использовать extern C простыми словами?

Возможно, я не понимаю различий между C и C ++, но когда и почему нам нужно использовать

extern "C" {

? По-видимому, это «соглашение о связях».

Я прочитал об этом вкратце и заметил, что все файлы заголовков .h, включенные в MSVS, окружают им свой код. Какой именно тип кода является «кодом C», а НЕ «кодом C ++»? Я думал, что C ++ включает весь код C?

Я предполагаю, что это не так, и что C ++ отличается, и что стандартные функции / функции существуют в одном или другом, но не в обоих (например, printf - это C, а cout - C ++), но этот C ++ имеет обратную совместимость, хотя объявление extern "C". Это правильно?

Мой следующий вопрос зависит от ответа на первый, но я все равно задам его здесь: поскольку файлы заголовков MSVS, написанные на C, окружены extern "C" {...}, когда вам когда-нибудь понадобится использовать это себя в собственном коде? Если ваш код является кодом C, и вы пытаетесь скомпилировать его в компиляторе C ++, не должно ли он работать без проблем, потому что все стандартные h-файлы, которые вы включаете, уже будут иметь в себе extern «C» с компилятором C ++?

Вы должны использовать это при компиляции на C ++, но при ссылке на уже созданные библиотеки C или что-то в этом роде?


person Russel    schedule 09.05.2010    source источник
comment
C ++ НЕ является надмножеством C. Существует допустимый C, который не является допустимым C ++.   -  person Yann Ramin    schedule 09.05.2010
comment
theatrus прав: stackoverflow.com/questions/2743572/   -  person nc3b    schedule 09.05.2010
comment
@YannRamin, вы должны связать некоторые полезные вещи, чтобы подтвердить свой аргумент.   -  person Michael Pacheco    schedule 10.02.2019


Ответы (7)


Вам нужно использовать extern "C" в C ++ при объявлении функции, которая была реализована / скомпилирована в C. Использование extern "C" сообщает компилятору / компоновщику использовать соглашения об именах и вызовах C вместо преобразования имен C ++ и соглашений о вызовах C ++, которые были бы используется иначе. Для функций, предоставляемых другими библиотеками, вам почти никогда не понадобится использовать extern "C", поскольку в хорошо написанных библиотеках это уже есть для общедоступных API, которые он экспортирует как на C, так и на C ++. Однако, если вы пишете библиотеку, которую хотите сделать доступной как на C, так и на C ++, вам придется условно поместить ее в свои заголовки.

Что касается того, является ли весь код C кодом C ++ ... нет, это неверно. Это популярный миф о том, что C ++ - это «надмножество C». Хотя C ++, безусловно, стремится быть максимально совместимым с C, есть некоторые несовместимости. Например, bool является допустимым C ++, но недействительным C, тогда как _Bool существует в C99, но недоступен в C ++.

Что касается того, нужно ли вам когда-нибудь использовать extern «C» с системными файлами «.h» ... любая хорошо спроектированная реализация будет иметь их там для вас, так что вам не нужно их использовать. Однако, чтобы быть уверенным, что они предоставлены, вы должны включить эквивалентный файл заголовка, который начинается с «c» и опускает «.h». Например, если вы включите ‹ctype.h›, почти в любую разумную систему будет добавлен extern «C»; однако, чтобы быть уверенным, что заголовок совместим с C ++, вы должны вместо этого включить заголовок ‹cctype›.

Вас также может заинтересовать Смешивание C и C ++ из C ++ FAQ Lite .

person Michael Aaron Safyan    schedule 09.05.2010
comment
Спасибо за отзывы всем. Так что мне нужно использовать это только при включении кода, который уже был скомпилирован в компиляторе C? - person Russel; 09.05.2010
comment
@Russel, да, при условии, что вы включаете его из C ++, и при условии, что заголовок еще не использует extern C. - person Michael Aaron Safyan; 09.05.2010
comment
Ах, вот почему вызов функции C в C ++ дает ошибки связывания и символов ... - person Abdel Aleem; 27.11.2019

Остальные ответы верны, но, вероятно, поможет полный "шаблонный" пример. Канонический метод включения кода C в проекты C и / или C ++ выглядит следующим образом:

//
// C_library.h
//

#ifdef __cplusplus
extern "C" {
#endif

//
// ... prototypes for C_library go here ...
//

#ifdef __cplusplus
}
#endif

-

//
// C_library.c
//

#include "C_library.h"

//
// ... implementations for C_library go here ...
//

-

//
// C++_code.cpp
//

#include "C_library.h"
#include "C++_code.h"

//
// ... C++_code implementation here may call C functions in C_library.c ...
//

Примечание: вышесказанное также относится к вызову кода C из Objective-C ++.

person Paul R    schedule 09.05.2010
comment
Отличный пример, спасибо. Я читал о соглашениях о вызовах для C ++ .. (_cdecl, _stdcall, _fastcall и т. Д.). Есть ли только одно соглашение о вызовах для C ?? - person Russel; 09.05.2010
comment
@Russel: _cdecl, _stdcall, _fastcall и т. Д. - все это нестандартные мерзости, специфичные для Windows, которых нет в цивилизованном мире. ;-) - person Paul R; 09.05.2010
comment
Прошу прощения :) Кто-нибудь может указать мне лучшую документацию по соглашениям о вызовах, чем MSDN? Кроме того, есть ли у C только одно стандартное соглашение о вызовах? - person Russel; 09.05.2010
comment
Да, стандартный C действительно имеет только одно соглашение о вызовах, но есть различные нестандартные расширения, с которыми вы можете столкнуться в дополнение к вышеупомянутым мерзостям Microsoft, такие как far и pascal, чтобы назвать два. - person Paul R; 09.05.2010

Компиляторы C ++ изменяют имена в своей таблице символов иначе, чем компиляторы C. Вам нужно использовать объявление extern "C", чтобы указать компилятору C ++ использовать соглашение об изменении языка C при построении таблицы символов.

person Ignacio Vazquez-Abrams    schedule 09.05.2010

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

person mmr    schedule 09.05.2010

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

#ifdef __cplusplus
extern "C" {
#endif

/* My library header */

#ifdef __cplusplus
} // extern
#endif
person Mike Weller    schedule 09.05.2010

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

  • У вас есть функция, реализованная на C, и вы хотите вызвать ее из C ++.

  • У вас есть функция, реализованная на C ++, и вы хотите вызвать ее из C. Обратите внимание, что в этом случае вы можете использовать только часть C C ++ в интерфейсе функции (без классов, ...).

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

Обычно объявления в заголовочном файле C окружены

#ifdef __cplusplus
extern "C" {
#endif

[... C declarations ...]

#ifdef __cplusplus
}
#endif

чтобы его можно было использовать с C ++.

person starblue    schedule 09.05.2010

Функции C ++ подвержены изменению имени. Это делает невозможным их вызов непосредственно из кода C, если не используется extern "C".

person Nathan Osman    schedule 09.05.2010