Каков эффект extern «C» в C ++?

Что именно делает extern "C" в C ++-коде?

Например:

 extern "C" { void foo(); } 

    extern «C» делает имя функции в C ++ имеет ссылку «C» (компилятор не изменяет имя), чтобы клиентский код C мог ссылаться на (т. е. использовать) вашу функцию, используя совместимый заголовочный файл «C», который содержит только объявление вашей функции. Определение вашей функции содержится в двоичном формате (который был скомпилирован вашим C ++-компилятором), который связывает клиентский C-linker с именем ‘C’.

    Поскольку C ++ имеет перегрузку имен функций, а C – нет, компилятор C ++ не может просто использовать имя функции как уникальный идентификатор для ссылки, поэтому он управляет именем, добавляя информацию о аргументах. Компилятору AC не нужно указывать имя, так как вы не можете перегружать имена функций в C. Когда вы указываете, что функция имеет ссылку extern «C» в C ++, компилятор C ++ не добавляет информацию о параметрах параметра / параметрах в имя, используемое для связь.

    Как вы знаете, вы можете указать связь «C» с каждым отдельным объявлением / определением явно или использовать блок для группировки последовательности объявлений / определений с определенной привязкой:

     extern "C" void foo(int); extern "C" { void g(char); int i; } 

    Если вы заботитесь о технических особенностях, они перечислены в разделе 7.5 стандарта C ++ 03, вот краткий обзор (с акцентом на extern «C»):

    • extern «C» – спецификация привязки
    • Каждый компилятор должен обеспечить связь «C»
    • спецификация привязки должна выполняться только в области пространства имен
    • все типы функций, имена функций и имена переменных имеют ссылку на язык См. комментарий Ричарда: только имена функций и имена переменных с внешней связью имеют ссылку на язык
    • два типа функций с различными языковыми связями являются различными типами, даже если в противном случае идентичны
    • привязка спецификаций, внутренняя определяет конечную связь
    • extern «C» игнорируется для членов classа
    • не более одной функции с определенным именем может иметь связь «C» (независимо от пространства имен)
    • extern «C» заставляет функцию иметь внешнюю связь (не может сделать ее статичной) См. комментарий Ричарда: «статический» внутри «extern» C «’действителен; объявленная сущность имеет внутреннюю связь, и поэтому не имеет языковой привязки
    • Связывание с C ++ с объектами, определенными на других языках, и объектами, определенными на C ++ с других языков, зависит от реализации и зависит от языка. Только там, где страtagsи размещения объектов двух языковых реализаций достаточно схожи, такая связь может быть достигнута

    Просто хотел добавить немного информации, так как я еще не видел ее.

    Вы очень часто видите код в заголовках C так:

     #ifdef __cplusplus extern "C" { #endif // all of your legacy C code here #ifdef __cplusplus } #endif 

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

    Хотя, я также видел код на C ++, например:

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

    который, как я полагаю, выполняет то же самое.

    Не уверен, какой путь лучше, но я видел и то, и другое.

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

    В C имя символа совпадает с именем функции. Это возможно, потому что в C две нестатические функции могут иметь одно и то же имя.

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

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

    Это удобно при использовании dlsym() и dlopen() для вызова таких функций.

    Декомпилируйте созданный g++ двоичный код, чтобы увидеть, что происходит

    Входные данные:

     void f() {} void g(); extern "C" { void ef() {} void eg(); } /* Prevent g and eg from being optimized away. */ void h() { g(); eg(); } 

    Компиляция с выходом GCC 4.8 Linux ELF:

     g++ -c a.cpp 

    Декомпилируйте таблицу символов:

     readelf -s ao 

    Вывод содержит:

     Num: Value Size Type Bind Vis Ndx Name 8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv 9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef 10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg 

    интерпретация

    Мы видим, что:

    • ef и, eg хранились в символах с тем же именем, что и в коде

    • другие символы были искалечены. Давайте развяжем их:

       $ c++filt _Z1fv f() $ c++filt _Z1hv h() $ c++filt _Z1gv g() 

    Вывод: оба следующих типа символов не были искажены:

    • определенный
    • объявленный, но неопределенный ( Ndx = UND ), который должен быть предоставлен по ссылке или времени выполнения из другого объектного файла

    Таким образом, вам понадобится extern "C" при вызове:

    • C из C ++: скажите g++ чтобы ожидать unmangled символы, произведенные gcc
    • C ++ из C: tell g++ чтобы генерировать unmangled символы для gcc для использования

    Вещи, которые не работают во внешнем C

    Становится очевидным, что любая функция C ++, требующая изменения имени, не будет работать внутри extern C :

     extern "C" { // Overloading. // error: declaration of C function 'void f(int)' conflicts with void f(); void f(int i); // Templates. // error: template with C linkage template  void f(C i) { } } 

    Минимальный запуск C из примера C ++

    Ради полноты и для новичков.

    Вызов C из C ++ довольно прост: каждая функция C имеет только один возможный символ без искажений, поэтому дополнительная работа не требуется.

    main.cpp:

     #include  #include "ch" int main() { assert(f() == 1); } 

    ч:

     #ifndef C_H #define C_H /* This ifdef allows the header to be used from both C and C++. */ #ifdef __cplusplus extern "C" { #endif int f(); #ifdef __cplusplus } #endif #endif 

    куб.см:

     #include "ch" int f() { return 1; } 

    Бег:

     g++ -c -o main.o -std=c++98 main.cpp gcc -c -o co -std=c89 cc g++ -o main.out main.o co ./main.out 

    Без extern "C" ссылка не работает:

     main.cpp:6: undefined reference to `f()' 

    потому что g++ ожидает найти искаженное f , которое gcc не произвело.

    Пример на GitHub .

    Минимальный допустимый C ++ из примера C

    Вызов C ++ из немного сложнее: мы должны вручную создавать неповрежденные версии каждой функции, которую мы хотим разоблачить.

    Здесь мы проиллюстрируем, как подвергать C ++ функции перегрузкам C.

    main.c:

     #include  #include "cpp.h" int main(void) { assert(f_int(1) == 2); assert(f_float(1.0) == 3); return 0; } 

    cpp.h:

     #ifndef CPP_H #define CPP_H #ifdef __cplusplus // C cannot see these overloaded prototypes, or else it would get confused. int f(int i); int f(float i); extern "C" { #endif int f_int(int i); int f_float(float i); #ifdef __cplusplus } #endif #endif 

    cpp.cpp:

     #include "cpp.h" int f(int i) { return i + 1; } int f(float i) { return i + 2; } int f_int(int i) { return f(i); } int f_float(float i) { return f(i); } 

    Бег:

     gcc -c -o main.o -std=c89 -Wextra main.c g++ -c -o cpp.o -std=c++98 cpp.cpp g++ -o main.out main.o cpp.o ./main.out 

    Без extern "C" он терпит неудачу:

     main.c:6: undefined reference to `f_int' main.c:7: undefined reference to `f_float' 

    потому что g++ генерирует искаженные символы, которые gcc не может найти.

    Пример на GitHub .

    C ++ управляет именами функций для создания объектно-ориентированного языка с процедурного языка

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

    Давайте посмотрим на следующий пример:

     #include  // Two functions are defined with the same name // but have different parameters void printMe(int a) { printf("int: %i\n", a); } void printMe(char a) { printf("char: %c\n", a); } int main() { printMe("a"); printMe(1); return 0; } 

    Компилятор AC не будет компилировать приведенный выше пример, потому что printMe та же функция printMe определена дважды (хотя они имеют разные параметры int a vs char a ).

    gcc -o printMe printMe.c && ./printMe;
    1. PrintMe определяется не один раз.

    Компилятор C ++ скомпилирует приведенный выше пример. Не важно, чтобы printMe определялся дважды.

    g ++ -o printMe printMe.c && ./printMe;

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

    Экстерн говорит: «Не подделывайте имена функций»

    Однако представьте себе, что у нас есть старый файл C с именем «parent.c», который include имена функций s из других устаревших файлов C «parent.h», «child.h» и т. Д. Если старый файл «parent.c» запускайте через компилятор C ++, тогда имена функций будут искажены, и они больше не будут соответствовать именам функций, указанным в «parent.h», «child.h» и т. д. – поэтому имена функций в этих внешних файлах должны будут быть искалеченным. И это может стать довольно грязным. Поэтому было бы удобно предоставить ключевое слово, которое может сказать компилятору C ++, чтобы он не исказил имя функции.

    Ключевое слово extern указывает компилятору C ++ не называть имена функций. Пример использования: extern void printMe(int a);

    Он изменяет связь функции таким образом, что функция может быть вызвана из C. На практике это означает, что имя функции не искажено .

    Ни один C-заголовок не будет компилироваться с помощью extern «C». Когда идентификаторы в C-заголовке конфликтуют с ключевыми словами C ++, компилятор C ++ будет жаловаться на это.

    Например, я видел, что следующий код не работает в g ​​++:

     extern "C" { struct method { int virtual; }; } 

    Kinda имеет смысл, но что-то нужно иметь в виду при переносе C-кода на C ++.

    Он информирует компилятор C ++ для поиска имен этих функций в стиле C при связывании, потому что имена функций, скомпилированных в C и C ++, различаются на этапе компоновки.

    extern «C» предназначен для распознавания компилятором C ++ и для уведомления компилятора о том, что отмеченная функция (или должна быть) скомпилирована в стиле C. Так что, связывая, он ссылается на правильную версию функции из C.

    Я использовал «extern» C »перед файлами DLL (динамической библиотеки ссылок), чтобы сделать и т. Д. Функция main ()« exportable », поэтому ее можно использовать позже в другом исполняемом файле из dll. Может быть, пример того, где я использовал его, может быть полезен.

    DLL

     #include  #include  using namespace std; #define DLL extern "C" __declspec(dllexport) //I defined DLL for dllexport function DLL main () { MessageBox(NULL,"Hi from DLL","DLL",MB_OK); } 

    EXE

     #include  #include  using namespace std; typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll Function mainDLLFunc;//make a variable for function placeholder int main() { char winDir[MAX_PATH];//will hold path of above dll GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe strcat(winDir,"\\exmple.dll");//concentrate dll name with path HINSTANCE DLL = LoadLibrary(winDir);//load example dll if(DLL==NULL) { FreeLibrary((HMODULE)DLL);//if load fails exit return 0; } mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main"); //defined variable is used to assign a function from dll //GetProcAddress is used to locate function with pre defined extern name "DLL" //and matcing function name if(mainDLLFunc==NULL) { FreeLibrary((HMODULE)DLL);//if it fails exit return 0; } mainDLLFunc();//run exported function FreeLibrary((HMODULE)DLL); } 

    extern "C" – спецификация связи, которая используется для вызова функций C в исходных файлах Cpp . Мы можем вызывать функции C, писать переменные и включать заголовки . Функция объявляется в extern entity и определяется вне. Синтаксис

    Тип 1:

     extern "language" function-prototype 

    Тип 2:

     extern "language" { function-prototype }; 

    например:

     #include using namespace std; extern "C" { #include // Include C Header int n; // Declare a Variable void func(int,int); // Declare a function (function prototype) } int main() { func(int a, int b); // Calling function . . . return 0; } // Function definition . . . void func(int m, int n) { // // } 

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

    Поэтому нам нужно использовать extern «C», чтобы отключить манипуляцию имени на C ++.

    Этот ответ для нетерпеливых / имеет крайние сроки для встречи, только часть / простое объяснение ниже:

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

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

    Чтобы изменить поведение C ++, то есть указать, что для определенной функции не должно выполняться манипуляция имени , вы можете использовать extern «C» перед именем функции по любой причине, например, экспортировать функцию с определенным именем из dll , для использования его клиентами.

    Прочтите другие ответы, чтобы получить более подробные / более правильные ответы.

    Interesting Posts

    Интеллектуальный способ удаления элементов из списка при перечислении в C #

    Передача функции хоста в качестве функции в функции __global__ OR __device__ в CUDA

    Папка отображается как один файл в Windows XP

    Не найден ресурс, который соответствует указанному имени ‘@ style / Theme.AppCompat.Light’

    Mac / Cocoa – получение списка окон с помощью API Accessibility

    Программа, созданная с помощью Java 8, может быть запущена на Java 7?

    Я думаю, что кто-то еще имеет доступ к моей беспроводной сети. Что дальше?

    ActionBar – пользовательский вид с центрированным ImageView, элементами действия по сторонам

    Отображение столбца PostgreSQL JSON для типа значения Hibernate

    Кнопка ускорения Accelerate / вкладка в окне Intel Rapid Storage

    Как сохранить пароль для подключенного сетевого диска?

    Как включить функции Multi Touch в Windows 7

    Мой коллега часто закрывает мою машину через ЛВС – как мне ее предотвратить?

    Изображение панорамы и масштабирования

    Какая петля быстрее, пока или для?

    Давайте будем гением компьютера.