Вопрос: Каков эффект extern «C» в C ++?


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

Например:

extern "C" {
   void foo();
}

1189


источник


Ответы:


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» - спецификация привязки
  • Каждый компилятор обязательный обеспечить связь «С»
  • спецификация привязки должна выполняться только в области пространства имен
  • все типы функций, имена функций и имена переменных имеют языковые связи См. Комментарий Ричарда: Только имена функций и имена переменных с внешней связью имеют языковые связи
  • два типа функций с различными языковыми связями являются различными типами, даже если в противном случае идентичны
  • привязка спецификаций, внутренняя определяет конечную связь
  • extern «C» игнорируется для членов класса
  • не более одной функции с определенным именем может иметь связь «C» (независимо от пространства имен)
  • extern «C» заставляет функцию иметь внешнюю связь (не может сделать ее статической) См. Комментарий Ричарда: «статический» внутри «extern» C «'действителен; объявленная сущность имеет внутреннюю связь, и поэтому не имеет языковой привязки
  • Связывание с C ++ с объектами, определенными на других языках, и объектами, определенными на C ++ с других языков, зависит от реализации и зависит от языка. Только там, где стратегии размещения объектов двух языковых реализаций достаточно схожи, такая связь может быть достигнута

1193



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

Вы очень часто видите код в заголовках 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"
}

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

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


234



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

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

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

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

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


162



Давайте декомпилировать файл объекта 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 a.o

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

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 ++: tell g++ожидать неперепутанных символов, созданных gcc
  • C ++ из C: tell g++для генерации неподключенных символов для 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 <class C> void f(C i) { }
}

113



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


20



Not any C-header will compile with extern "C". When identifiers in a C-header conflict with C++ keywords the C++ compiler will complain about this.

For example, I have seen the following code fail in a g++ :

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

Kinda makes sense, but is something to keep in mind when porting C-code to C++.


20



C++ mangles function names to create an object-oriented language from a procedural language

Most programming languages aren't built on-top of existing programming languages. C++ is built on-top of C, and furthermore it's an object-oriented programming language built from a procedural programming language, and for that reason there are C++ keywords like extern which provide backwards compatibility with C.

Let's look at the following example:

#include <stdio.h>

// 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;
}

A C compiler will not compile the above example, because the same function printMe is defined twice (even though they have different parameters int a vs char a).

gcc -o printMe printMe.c && ./printMe;
1 error. PrintMe is defined more than once.

A C++ compiler will compile the above example. It does not care that printMe is defined twice.

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

This is because a C++ compiler implicitly renames (mangles) functions based on their parameters. In C, this feature was not supported. However, when C++ was built over C, the language was designed to be object-oriented, and needed to support the ability to create different classes with methods (functions) of the same name, and to override methods (method overriding) based on different parameters.

Extern says "don't mangle function names"

However, imagine we have a legacy C file named "parent.c" that includes function names from other legacy C files, "parent.h", "child.h", etc. If the legacy "parent.c" file is run through a C++ compiler, then the function names will be mangled, and they will no longer match the function names specified in "parent.h", "child.h", etc - so the function names in those external files would need to be mangled as well. And this could become quite messy. So it might be convenient to provide a keyword which can tell the C++ compiler not to mangle a function name.

The extern keyword tells a C++ compiler not to mangle (rename) function names. Example usage: extern void printMe(int a);


19



It informs the C++ compiler to look up the names of those functions in a C-style when linking, because the names of functions compiled in C and C++ are different during the linking stage.


15



extern "C" is meant to be recognized by a C++ compiler and to notify the compiler that the noted function is (or to be) compiled in C style. So that while linking, it link to the correct version of function from C.


12



I used 'extern "C"' before for dll(dynamic link library) files to make etc. main() function "exportable" so it can be used later in another executable from dll. Maybe an example of where I used to use it can be useful.

DLL

#include <string.h>
#include <windows.h>

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 <string.h>
#include <windows.h>

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);
}

5