Вопрос по c++, c – Какое влияние оказывает extern «C» в C ++?

1360

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

Например:

extern "C" {
   void foo();
}
@Litherum В верхней части моей головы, это говорит компилятору компилировать эту область кода с использованием C, учитывая, что у вас есть кросс-компилятор. Кроме того, это означает, что у вас есть файл Cpp, где у вас есть этоfoo() функция. ha9u63ar
Я хотел бы представить вам эту статью:http://www.agner.org/optimize/calling_conventions.pdf Он говорит вам гораздо больше о соглашении о вызовах и разнице между компиляторами. Sam Liao

Ваш Ответ

13   ответов
271

так как я еще не видел ее опубликованной.

Вы очень часто будете видеть код в заголовках C, например:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Это позволяет вам использовать этот заголовочный файл C вместе с кодом C ++, поскольку макрос & quot; __cplusplus & quot; будет определено. Но вы можетеalso по-прежнему использовать его с вашим старым кодом C, где макросNOT определены, поэтому он не увидит уникальную конструкцию C ++.

Хотя я также видел код C ++, такой как:

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

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

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededextern "C"Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededg++Error: User Rate Limit Exceededextern "C"Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededextern "C"Error: User Rate Limit Exceeded
5

extern "C" является спецификацией связи, которая используется дляcall C functions вCpp source files, Мы можемcall C functions, write Variables, & include headers, Функция объявлена во внешнем объекте & amp; это определено снаружи. Синтаксис

Type 1:

extern "language" function-prototype

Type 2:

extern "language"
{
     function-prototype
};

eg:

#include<iostream>
using namespace std;

extern "C"
{
     #include<stdio.h>    // 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)
{
    //
    //
}
26

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededen.wikipedia.org/wiki/Name_mangling
2

только часть / простое объяснение ниже:

in C++, you can have same name in class via overloading (for example, since they are all same name can't be exported as-is from dll, etc.) solution to these problems is they are converted to different strings (called symbols), symbols accounts the name of function, also the arguments, so each of these functions even with same name, can be uniquely identified (also called, name mangling) in C, you don't have overloading, the function name is unique (so, a separate string for identifying the a function name uniquely is not required, so symbol is function name itself)

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

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

Читайте другие ответы, для более подробных / более правильных ответов.

6

прежде, чем файлы dll (библиотеки динамической компоновки) могли сделать функцию main () & quot; экспортируемой & quot; поэтому он может быть использован позже в другом исполняемом файле из DLL. Может быть, пример того, где я использовал его, может быть полезным.

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);
}
Error: User Rate Limit ExceededappearsError: User Rate Limit ExceedednotError: User Rate Limit Exceeded"working"Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded"to prevent stack corruption on run"Error: User Rate Limit Exceededvoid*Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededextern "C"Error: User Rate Limit Exceeded__declspec(dllexport)Error: User Rate Limit ExceededmainError: User Rate Limit ExceededWINAPIError: User Rate Limit Exceeded_mainError: User Rate Limit Exceeded[email protected]Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
1341

linkage (компилятор не искажает имя), чтобы клиентский код C мог связать (т.е. использовать) вашу функцию, используя «C»; совместимый заголовочный файл, который содержит только объявление вашей функции. Ваше определение функции содержится в двоичном формате (который был скомпилирован вашим компилятором C ++), который клиент 'C' apos; затем линкер будет ссылаться на использование «C»; название.

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

Чтобы вы знали, вы можете указать & quot; C & quot; привязка к каждому отдельному объявлению / определению явно или использование блока для группировки последовательности объявлений / определений, чтобы иметь определенную связь:

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

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

extern "C" is a linkage-specification Every compiler is required to provide "C" linkage a linkage specification shall occur only in namespace scope all function types, function names and variable names have a language linkage See Richard's Comment: Only function names and variable names with external linkage have a language linkage two function types with distinct language linkages are distinct types even if otherwise identical linkage specs nest, inner one determines the final linkage extern "C" is ignored for class members at most one function with a particular name can have "C" linkage (regardless of namespace) extern "C" forces a function to have external linkage (cannot make it static) See Richard's comment: 'static' inside 'extern "C"' is valid; an entity so declared has internal linkage, and so does not have a language linkage Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
25

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

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

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

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

Error: User Rate Limit ExceededanyError: User Rate Limit Exceeded
extern "C"Error: User Rate Limit Exceededint virtual;Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
165

Decompile a g++ generated binary to see what is going on

main.cpp

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 LinuxELF выход:

g++ -c main.cpp

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

readelf -s main.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

Interpretation

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

ef and eg were stored in symbols with the same name as in the code

the other symbols were mangled. Let's unmangle them:

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

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

defined declared but undefined (Ndx = UND), to be provided at link or run time from another object file

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

C from C++: tell g++ to expect unmangled symbols produced by gcc C++ from C: tell g++ to generate unmangled symbols for gcc to use

Things that do not work in extern 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) { }
}

Minimal runnable C from C++ example

Для полноты картины и для новичков смотрите также:Как использовать исходные файлы C в проекте C ++?

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

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

c.h

#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 "c.h"

int f(void) { return 1; }

Бежать:

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

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

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

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

Пример на GitHub.

Minimal runnable C++ from C example

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

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

main.c

#include <assert.h>

#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.

Проверено в Ubuntu 18.04.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededextern "C" {Error: User Rate Limit Exceededunmangled C functions from within C++ programsError: User Rate Limit Exceededunmangled C++ functions from within C programsError: User Rate Limit ExceededandError: User Rate Limit Exceeded
1

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

Поэтому нам нужно использовать extern & quot; C & quot; отключить искажение имени в C ++.

13

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

190

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

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

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

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

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
35

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

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

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

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

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

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

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

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

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

Extern says "don't mangle function names"

Однако представьте, что у нас есть устаревший C-файл с именем & quot; parent.c & quot; тотincludes имена функций из других устаревших файлов C, "parent.h", "quot; child.h" и т. д. Если унаследованный "parent.c" файл запускается через компилятор C ++, затем имена функций будут искажены, и они больше не будут совпадать с именами функций, указанными в & quot; parent.h & quot ;, & quot; child.h & quot ;, и т. д., поэтому имена функций в этих внешних файлы также должны быть искажены. Переназначение имен функций в сложной программе на C, имеющих много зависимостей, может привести к поломке кода; поэтому может быть удобно предоставить ключевое слово, которое может сказать компилятору C ++ не искажать имя функции.

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededextern "C"Error: User Rate Limit ExceededdllError: User Rate Limit Exceeded
19

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

Похожие вопросы