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

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

Например:

extern "C" {
   void foo();
}
25.06.2009 02:10:07
Я хотел бы представить вам эту статью: http://www.agner.org/optimize/calling_conventions.pdf. Она расскажет вам гораздо больше о соглашении о вызовах и разнице между компиляторами.
Sam Liao 25.06.2009 02:18:24
@Litherum В верхней части моей головы, это говорит компилятору компилировать эту область кода с использованием C, учитывая, что у вас есть кросс-компилятор. Кроме того, это означает, что у вас есть Cpp-файл, в котором у вас есть эта foo()функция.
ha9u63ar 27.06.2013 08:18:51
@ ha9u63ar Это «с моей головы». Весь остальной ваш комментарий тоже неверен. Я рекомендую вам удалить его.
TamaMcGlinn 9.04.2020 10:01:22
15 ОТВЕТОВ
РЕШЕНИЕ

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 ++ из других языков, определяется реализацией и зависит от языка. Только там, где стратегии размещения объектов двух языковых реализаций достаточно похожи, такая связь может быть достигнута
1533
26.04.2019 13:21:55
Компилятор C не использует искажения, которые использует c ++. Поэтому, если вы хотите вызвать интерфейс ac из программы на c ++, вы должны четко объявить, что интерфейс c является «extern c».
Sam Liao 25.06.2009 02:28:55
@Faisal: не пытайтесь связывать код, созданный с помощью различных компиляторов C ++, даже если все перекрестные ссылки являются внешними «C». Часто существуют различия между макетами классов, или механизмами, используемыми для обработки исключений, или механизмами, используемыми для обеспечения инициализации переменных перед использованием, или другими такими различиями, плюс вам могут понадобиться две отдельные библиотеки поддержки времени выполнения C ++ (одна для каждый компилятор).
Jonathan Leffler 25.06.2009 03:24:42
'extern "C" заставляет функцию иметь внешнюю связь (не может сделать ее статичной)' неверно. 'static' inside 'extern "C"' допустим; объявленная таким образом сущность имеет внутреннюю связь и поэтому не имеет языковой связи.
Richard Smith 14.02.2013 04:06:43
«все типы функций, имена функций и имена переменных имеют языковую связь» также неверно. Только имена функций и имена переменных с внешней связью имеют языковую связь.
Richard Smith 14.02.2013 04:07:14
Обратите внимание, что extern "C" { int i; }это определение. Это может быть не то, что вы хотели, рядом с не определением void g(char);. Чтобы сделать это без определения, вам нужно extern "C" { extern int i; }. С другой стороны, синтаксис одного объявления без фигурных скобок делает объявление не определением: extern "C" int i;это то же самое, что иextern "C" { extern int i; }
aschepler 1.07.2014 16:39:02

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

19
25.06.2009 02:12:12

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

27
25.06.2009 02:12:21
Mangled - это термин, который обычно используется ... Не верьте, что я когда-либо видел "украшенный", используемый с этим значением.
Matthew Scharley 25.06.2009 02:17:51
Microsoft (по крайней мере, частично) использует оформленные, а не искаженные в своей документации. они даже называют свой инструмент для декорирования (иначе говоря, un-mangle) имени undname.
René Nyffenegger 31.01.2020 21:41:47

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

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

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

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

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

203
3.03.2018 15:48:25
что вы имеете в виду под рукой? символ name = имя функции сделает имя символа переданным в dlsym или другое?
Error 3.03.2018 10:06:58
@ Ошибка: да. В общем случае в общем случае невозможно dlopen () совместно используемой библиотеки C ++, имеющей только заголовочный файл, и выбрать правильную функцию для загрузки. (На x86 есть опубликованная спецификация распределения имен в форме Itanium ABI, которую все известные мне компиляторы x86 используют для манипулирования именами функций C ++, но ничего в языке не требует этого.)
Jonathan Tomer 23.04.2018 22:23:35

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

12
10.04.2012 09:46:10

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

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

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

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

320
21.10.2012 01:08:32
Существует четкая разница. В первом случае, если вы скомпилируете этот файл с помощью обычного компилятора gcc, он сгенерирует объект, имя которого не искажено. Если затем связать объекты C и C ++ с компоновщиком, он НЕ найдет функции. Вам нужно будет включить эти файлы «устаревшего заголовка» с ключевым словом extern, как во втором блоке кода.
Anne van Rossum 12.04.2013 14:00:17
@Anne: Компилятор C ++ также будет искать не исправленные имена, потому что он видел extern "C"в заголовке). Он прекрасно работает, использовал эту технику много раз.
Ben Voigt 27.06.2014 05:34:35
@Anne: Это не правильно, первое тоже хорошо. Он игнорируется компилятором C и имеет тот же эффект, что и второй в C ++. Компилятору все равно, встречается ли он extern "C"до или после включения заголовка. К тому времени, когда он достигает компилятора, это все равно только один длинный поток предварительно обработанного текста.
Ben Voigt 30.06.2014 15:54:26
@ Анна, нет, я думаю, что на вас повлияла какая-то другая ошибка в источнике, потому что то, что вы описываете, неверно. Ни одна из версий g++не ошиблась ни по одной цели, ни за последние 17 лет, по крайней мере. Суть первого примера в том, что не имеет значения, используете ли вы компилятор C или C ++, для имен в extern "C"блоке не будет производиться искажение имен .
Jonathan Wakely 19.01.2016 20:45:33
«какой из них лучше» - наверняка, первый вариант лучше: он позволяет включать заголовок напрямую, без каких-либо дополнительных требований, как в коде на C, так и на C ++. Второй подход - это обходной путь для заголовков C, автор забыл о защитных средствах C ++ (однако нет проблем, если они добавляются впоследствии, допускаются вложенные объявления extern "C" ...).
Aconcagua 9.08.2017 09:23:07

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

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

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

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

28
5.02.2019 04:38:00
extern "C"означает использовать связь C, как описано в других ответах. Это не значит «компилировать содержимое как C» или что-то еще. int virtual;недопустимо в C ++, и указание другой связи не меняет этого.
M.M 26.01.2015 22:26:05
... или режим вообще, любой код, содержащий синтаксическую ошибку, не будет компилироваться.
Valentin Heinitz 1.11.2017 10:33:51
@ValentinHeinitz, естественно, хотя использование «виртуального» в качестве идентификатора в C не является синтаксической ошибкой. Я просто хотел указать, что вы не можете автоматически использовать любой заголовок C в C ++, поместив вокруг него extern "C".
Sander Mertens 2.11.2017 14:35:51

Ранее я использовал 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);
}
6
1.07.2014 15:43:34
Богус. extern "C"и __declspec(dllexport)не связаны. Первый контролирует оформление символов, последний отвечает за создание экспортной записи. Вы также можете экспортировать символ, используя оформление имени C ++. Помимо полного упущения из этого вопроса, есть и другие ошибки в примере кода. Например, при mainэкспорте из вашей DLL возвращаемое значение не объявляется. Или созыв соглашения, в этом отношении. При импорте вы приписываете соглашение о случайном вызове ( WINAPI) и используете неправильный символ для 32-битных сборок (должно быть _mainили _main@0). Извините, -1.
IInspectable 7.09.2016 08:28:26
Это лишь повторяет, что вы не знаете, что вы делаете, но делать это таким образом, кажется, работает для вас, для какого-то нераскрытого списка целевых платформ. Вы не рассмотрели вопросы, которые я поднял в моем предыдущем комментарии. Это все-таки голосование против, из-за того, что он был совершенно не прав (есть еще то, что не вписалось ни в один комментарий).
IInspectable 25.05.2017 09:26:38
Публикация ответа на Stack Overflow подразумевает, что вы знаете, что делаете. Это ожидается. Что касается вашей попытки «предотвратить повреждение стека при запуске» : ваша сигнатура функции указывает возвращаемое значение типа void*, но ваша реализация ничего не возвращает. Это будет очень хорошо летать ...
IInspectable 25.05.2017 10:40:22
Если вы реализуете что-то, что, кажется, работает, по чистой случайности, тогда вы явно не знаете, что делаете (ваш «рабочий» образец попадает в эту категорию). Это неопределенное поведение, и появление на работе является допустимой формой неопределенного поведения. Это все еще не определено. Я был бы очень признателен, если бы вы проявили больше усердия в будущем. Частично это может быть удаление предложенного ответа.
IInspectable 25.05.2017 11:23:06
Вы переосмысливаете функцию, которая ничего не возвращает, как функцию, которая возвращает указатель. К счастью, x86 очень простителен в отношении несоответствия сигнатур функций и, в частности, возвращаемых значений целочисленного типа. Ваш код работает только по совпадению. Если вы не согласны, вам нужно объяснить, почему ваш код работает надежно.
IInspectable 25.05.2017 12:36:39

Декомпилируйте 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.

253
20.02.2020 11:45:47
Лучший ответ, поскольку вы 1) явно упомянули, что это extern "C" {помогает вам вызывать неискаженные функции C изнутри программ на C ++ , а также неуправляемые функции C ++ изнутри программ C , что в других ответах не так очевидно, и 2) потому что вы демонстрируете различные примеры каждый. Спасибо!
Gabriel Staples 25.10.2018 17:51:21
Мне очень нравится этот ответ
selfboot 9.04.2019 03:45:23
Передайте лучший ответ, поскольку он показывает, как вызывать перегруженные функции из c
Gaspa79 24.07.2019 13:30:01
@JaveneCPPMcGowan, что заставляет вас думать, что у меня есть учитель C ++? :-)
Ciro Santilli 冠状病毒审查六四事件法轮功 1.11.2019 08:39:49

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)
{
    //
    //
}
5
17.11.2015 12:09:03

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

52
1.11.2019 04:50:10
мы не можем использовать, extern "C"если у нас есть только dllфайл? Я имею в виду, если у нас нет заголовочного файла, а есть только исходный файл (только реализации) и использование его функции через указатель на функцию. в этом состоянии мы просто использовали функции (независимо от их имени).
BattleTested 4.11.2018 07:17:26
@tfmontague, для меня ты прибил это правильно! прямо в голову.
Artanis Zeratul 11.12.2018 03:28:39

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

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

1
7.08.2017 07:54:12

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

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

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

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

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

3
24.07.2018 13:41:42

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

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

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

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

0
16.02.2020 01:22:50
Добро пожаловать в переполнение стека. Если вы решите ответить на старый вопрос, на котором уже есть точные и правильные ответы, добавление нового ответа в конце дня может не принести вам никакой пользы. Если у вас есть какая-то новая отличительная информация или вы убеждены, что все остальные ответы неверны, обязательно добавьте новый ответ, но «еще один ответ», дающий ту же основную информацию спустя долгое время после того, как вопрос был задан, обычно выигрывает ». Я не заработаю тебе много кредитов. Честно говоря, я не думаю, что в этом ответе есть что-то новое.
Jonathan Leffler 16.02.2020 01:25:25
Ну, я должен был вспомнить вашу точку зрения - хорошо
Susobhan Das 16.02.2020 13:19:49

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

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

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

-1
4.03.2020 17:53:48
Компилятор C ++ может скомпилировать extern "C"функцию - и (с учетом некоторых ограничений) она будет вызываться кодом, скомпилированным компилятором C.
Jonathan Leffler 5.03.2020 16:47:13