Каково влияние extern «C» в C ++?


1632

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

Например:

extern "C" {
   void foo();
}

83
Я хотел бы представить вам эту статью: http://www.agner.org/optimize/calling_conventions.pdf. Она расскажет вам гораздо больше о соглашении о вызовах и разнице между компиляторами.
Сэм Ляо

1
@Litherum В верхней части моей головы, это говорит компилятору компилировать эту область кода с использованием C, учитывая, что у вас есть кросс-компилятор. Кроме того, это означает, что у вас есть Cpp-файл, в котором у вас есть эта foo()функция.
ha9u63ar

1
@ ha9u63ar Это «с моей головы». Весь остальной ваш комментарий тоже неверен. Я рекомендую вам удалить его.
TamaMcGlinn

Ответы:


1560

extern "C" делает имя функции в C ++ связанным с C (компилятор не искажает имя), так что клиентский код C может ссылаться (т.е. использовать) вашу функцию, используя совместимый с C заголовочный файл, который содержит только декларация вашей функции. Определение вашей функции содержится в двоичном формате (который был скомпилирован вашим компилятором C ++), на который клиентский компоновщик 'C' затем будет ссылаться, используя имя 'C'.

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

22
Компилятор C не использует искажения, которые использует c ++. Поэтому, если вы хотите вызвать интерфейс ac из программы на c ++, вы должны четко объявить, что интерфейс c является «extern c».
Сэм Ляо

59
@Faisal: не пытайтесь связывать код, созданный с помощью различных компиляторов C ++, даже если все перекрестные ссылки являются «внешними« C »». Часто существуют различия между макетами классов, или механизмами, используемыми для обработки исключений, или механизмами, используемыми для обеспечения инициализации переменных перед использованием, или другими такими различиями, плюс вам могут понадобиться две отдельные библиотеки поддержки времени выполнения C ++ (одна для каждый компилятор).
Джонатан Леффлер

8
'extern "C" заставляет функцию иметь внешнюю связь (не может сделать ее статичной)' неверно. 'static' inside 'extern "C"' допустим; объявленная таким образом сущность имеет внутреннюю связь и поэтому не имеет языковой связи.
Ричард Смит

14
«все типы функций, имена функций и имена переменных имеют языковую связь» также неверно. Только имена функций и имена переменных с внешней связью имеют языковую связь.
Ричард Смит

9
Обратите внимание, что extern "C" { int i; }это определение. Это может быть не то, что вы хотели, рядом с не определением void g(char);. Чтобы сделать это без определения, вам нужно extern "C" { extern int i; }. С другой стороны, синтаксис одного объявления без фигурных скобок делает объявление не определением: extern "C" int i;это то же самое, чтоextern "C" { extern int i; }
aschepler

327

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

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

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

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


11
Существует четкая разница. В первом случае, если вы скомпилируете этот файл с помощью обычного компилятора gcc, он сгенерирует объект, имя которого не искажено. Если затем связать объекты C и C ++ с компоновщиком, он НЕ найдет функции. Вам нужно будет включить эти файлы «устаревшего заголовка» с ключевым словом extern, как во втором блоке кода.
Анна Ван Россум

8
@Anne: Компилятор C ++ также будет искать не исправленные имена, потому что он видел extern "C"в заголовке). Он прекрасно работает, использовал эту технику много раз.
Бен Фойгт

20
@Anne: Это не правильно, первое тоже хорошо. Он игнорируется компилятором C и имеет тот же эффект, что и второй в C ++. Компилятору все равно, встречается ли он extern "C"до или после включения заголовка. К тому времени, когда он достигает компилятора, это всего лишь один длинный поток предварительно обработанного текста.
Бен Фойгт

8
@ Анна, нет, я думаю, что на вас повлияла какая-то другая ошибка в источнике, потому что то, что вы описываете, неверно. Ни одна из версий g++не ошиблась ни по одной цели, ни за последние 17 лет, по крайней мере. Суть первого примера в том, что не имеет значения, используете ли вы компилятор C или C ++, для имен в extern "C"блоке не будет производиться искажение имен .
Джонатан Уэйкли

7
«какой из них лучше» - наверняка, первый вариант лучше: он позволяет включать заголовок напрямую, без каких-либо дополнительных требований, как в коде на C, так и на C ++. Второй подход - это обходной путь для заголовков C, автор забыл о защитных средствах C ++ (однако, нет проблем, если они добавляются впоследствии, допускаются вложенные объявления extern "C" ...).
Аконкагуа,

267

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

main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

Скомпилируйте и разберите сгенерированный вывод ELF :

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

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

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 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++чтобы ожидать непогашенные символы, произведенныеgcc
  • C ++ от C: сказать, g++чтобы генерировать не исправленные символы для gccиспользования

Вещи, которые не работают в 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) { }
}

Пример минимального запуска C из C ++

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

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

main.cpp

#include <cassert>

#include "c.h"

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

ч

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#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 .

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

Вызов C ++ из 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.


21
Лучший ответ, так как вы 1) явно упомянули, что это extern "C" {помогает вам вызывать неупорядоченные функции C изнутри программ на C ++ , а также неупорядоченные функции C ++ изнутри программ на C , что в других ответах не так очевидно, и 2) потому что вы демонстрируете различные примеры каждый. Спасибо!
Габриэль Стейплс

3
Мне очень нравится этот ответ
самостоятельная загрузка

4
Передайте лучший ответ, поскольку он показывает, как вызывать перегруженные функции из c
Gaspa79

1
@JaveneCPPMcGowan, что заставляет вас думать, что у меня есть учитель C ++? :-)
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

205

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

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

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

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

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


что вы имеете в виду под рукой? символ name = имя функции сделает имя символа переданным в dlsym или другое?
Ошибка

1
@ Ошибка: да. В общем случае в общем случае невозможно dlopen () совместно используемой библиотеки C ++, имеющей только заголовочный файл, и выбрать правильную функцию для загрузки. (На x86 есть опубликованная спецификация распределения имен в форме Itanium ABI, которую все известные мне компиляторы x86 используют для манипулирования именами функций C ++, но ничего в языке не требует этого.)
Джонатан Томер,

52

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

Большинство языков программирования не построены поверх существующих языков программирования. C ++ построен поверх C, и, кроме того, это объектно-ориентированный язык программирования, построенный из процедурного языка программирования, и по этой причине существуют такие выражения C ++, какextern "C" которые обеспечивают обратную совместимость с 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;
}

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

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

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

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

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

extern "C" говорит "не калечить имена функций C"

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

extern "C"Ключевое слово указывает на ++ компилятор C не калечить (переименования) C имена функций.

Например:

extern "C" void printMe(int a);


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

@tfmontague, для меня ты прибил это правильно! прямо в голову.
Артанис Зератул

29

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

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

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

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


14
extern "C"означает использовать связь C, как описано в других ответах. Это не значит «компилировать содержимое как C» или что-то еще. int virtual;недопустимо в C ++, и указание другой связи не меняет этого.
ММ

1
... или режим вообще, любой код, содержащий синтаксическую ошибку, не будет компилироваться.
Валентин Хайниц

4
@ValentinHeinitz, естественно, хотя использование «виртуального» в качестве идентификатора в C не является синтаксической ошибкой. Я просто хотел указать, что вы не можете автоматически использовать любой заголовок C в C ++, поместив вокруг него extern "C".
Сандер Мертенс

28

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


3
Mangled - это термин, который обычно используется ... Не верьте, что я когда-либо видел "украшенный", использованный с этим значением.
Мэтью Шарли

1
Microsoft (по крайней мере, частично) использует оформленные, а не искаженные в своей документации. они даже называют свой инструмент для декорирования (иначе говоря, un-mangle) имени undname.
Рене Ниффенеггер

20

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


12

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


6

Ранее я использовал extern "C" для файлов dll (динамически подключаемых библиотек), чтобы сделать функцию main () и т. Д. "Экспортируемой", чтобы позже ее можно было использовать в другом исполняемом файле из 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);
}

4
Богус. extern "C"и __declspec(dllexport)не связаны. Первый контролирует оформление символов, последний отвечает за создание экспортной записи. Вы также можете экспортировать символ, используя оформление имени C ++. Помимо полного отсутствия смысла в этом вопросе, в примере кода есть и другие ошибки. Например, при mainэкспорте из вашей DLL возвращаемое значение не объявляется. Или созыв соглашения, в этом отношении. При импорте вы приписываете соглашение о случайном вызове ( WINAPI) и используете неправильный символ для 32-битных сборок (должно быть _mainили _main@0). Извините, -1.
II

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

1
Публикация ответа на Stack Overflow подразумевает, что вы знаете, что делаете. Это ожидается. Что касается вашей попытки «предотвратить повреждение стека при запуске» : ваша сигнатура функции указывает возвращаемое значение типа void*, но ваша реализация ничего не возвращает. Это будет очень хорошо летать ...
IInspectable

1
Если вы реализуете что-то, что, кажется, работает, по чистой случайности, то вы явно делаете не знаете, что делаете (ваш «рабочий» образец попадает в эту категорию). Это неопределенное поведение, и появление на работе является допустимой формой неопределенного поведения. Это все еще не определено. Я был бы очень признателен, если бы вы проявили больше усердия в будущем. Частично это может быть удаление предложенного ответа.
Инспектируемый

1
Вы переосмысливаете функцию, которая ничего не возвращает, как функцию, которая возвращает указатель. К счастью, x86 очень простителен в отношении несоответствия сигнатур функций и, в частности, возвращаемых значений целочисленного типа. Ваш код работает только по совпадению. Если вы не согласны, вам нужно объяснить, почему ваш код работает надежно.
Инспектируемый

5

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

Тип 1:

extern "language" function-prototype

Тип 2:

extern "language"
{
     function-prototype
};

например:

#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)
{
    //
    //
}

3

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

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

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

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

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


1

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

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


0

Не противореча другим хорошим ответам, я добавлю немного своего примера.

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

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

Причина (мы вызываем реализацию C из C ++): либо мы хотим вызвать функцию C из C ++, либо вызвать функцию C ++ из C (классы C ++ ... и т. Д. Не работают в C).


Добро пожаловать в стек переполнения. Если вы решите ответить на более старый вопрос, на котором уже есть точные и правильные ответы, добавление нового ответа в конце дня может не принести вам никакой пользы. Если у вас есть какая-то отличительная новая информация, или вы убеждены, что все остальные ответы неверны, обязательно добавьте новый ответ, но «еще один ответ», дающий ту же основную информацию спустя долгое время после того, как вопрос был задан, обычно выигрывает ». Я не заработаю тебе много кредитов. Честно говоря, я не думаю, что в этом ответе есть что-то новое.
Джонатан Леффлер

Ну, я должен был вспомнить вашу точку зрения - хорошо
Susobhan Das

-1

Функция void f (), скомпилированная компилятором C, и функция с тем же именем void f (), скомпилированная компилятором C ++, не являются одной и той же функцией. Если вы написали эту функцию на C, а затем попытались вызвать ее из C ++, то компоновщик будет искать функцию C ++ и не найдет функцию C.

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

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


Компилятор C ++ может скомпилировать extern "C"функцию - и (с некоторыми ограничениями) она будет вызываться кодом, скомпилированным компилятором C.
Джонатан Леффлер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.