Яка дія зовнішнього “C” на C ++?


1630

Що саме робить введення extern "C"коду на C ++?

Наприклад:

extern "C" {
   void foo();
}

82
Я хотів би ознайомити вас із цією статтею: http://www.agner.org/optimize/calling_conventions.pdf Він розповідає вам набагато більше про конвенцію про виклики та про різницю між компіляторами.
Сем Ляо

1
@Litherum У верхній частині моєї голови, він повідомляє компілятору скласти цю область коду за допомогою C, враховуючи, що у вас є крос-компілятор. Також це означає, що у вас є файл Cpp, де ви маєте цю foo()функцію.
ha9u63ar

1
@ ha9u63ar Це "у верхній частині моєї голови". Весь решта ваших коментарів також неправильна. Рекомендую видалити його.
TamaMcGlinn

Відповіді:


1557

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

Оскільки C ++ має перевантаження імен функцій, а C - ні, компілятор C ++ не може просто використовувати ім'я функції як унікальний ідентифікатор для посилання, тому він керує ім'ям, додаючи інформацію про аргументи. Компілятору змінного струму не потрібно маніпулювати назвою, оскільки ви не можете перевантажувати імена функцій у C. Коли ви заявляєте, що функція має зовнішню зв'язок "C" у C ++, компілятор C ++ не додає інформацію про аргументи / параметри до імені, використовуваного для зв’язок.

Просто ви знаєте, що ви можете вказати посилання "C" на кожне окреме оголошення / визначення явно або використовувати блок для групування послідовності декларацій / визначень, щоб мати певний зв'язок:

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

Якщо ви дбаєте про технічні характеристики, вони перераховані в розділі 7.5 стандарту C ++ 03, ось короткий підсумок (з акцентом на зовнішнє "C"):

  • extern "C" - специфікація зв'язку
  • Кожен компілятор зобов'язаний забезпечити зв'язок "С"
  • специфікація зв'язку має відбуватися лише в області простору імен
  • всі типи функцій, назви функцій та назви змінних мають зв’язок мови. Див. коментар Річарда: Тільки імена функцій та назви змінних із зовнішньою зв'язком мають мову.
  • два типи функцій з чіткими мовними зв’язками є різними типами, навіть якщо інакше однакові
  • специфікація гнізд гніздо, внутрішня визначає остаточну зв'язок
  • зовнішній "C" ігнорується для членів класу
  • щонайменше одна функція з певним іменем може мати зв'язок "C" (незалежно від простору імен)
  • зовнішній "C" змушує функцію мати зовнішню зв'язок (не може зробити її статичною) Див. коментар Річарда: "статичний" всередині "зовнішній" С "дійсний; суб'єкт господарювання, який так заявлений, має внутрішню зв'язок, і тому не має мови
  • Зв'язок від C ++ до об'єктів, визначених іншими мовами, та до об'єктів, визначених у C ++ з інших мов, визначається реалізацією та залежить від мови. Тільки там, де стратегії компонування об’єктів у двох мовних реалізаціях є досить подібними, можна досягти такого зв'язку

21
Компілятор C не використовує mangling, який робить c ++. Отже, якщо ви хочете викликати інтерфейс змінного струму з програми c ++, ви повинні чітко оголосити, що інтерфейс c є "зовнішнім c".
Сем Ляо

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

8
"зовнішній" C "примушує функцію мати зовнішній зв'язок (не може зробити його статичним)" неправильно. 'статичний' всередині 'зовнішній' 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

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

Ви дуже часто бачите код у заголовках С так:

#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
@Anne, ні, я думаю, на вас вплинула якась інша помилка в джерелі, тому що те, що ви описуєте, є неправильним. Жодна версія g++не помилилась на будь-яку ціль, принаймні, протягом останніх 17 років. Вся суть першого прикладу полягає в тому, що не має значення, чи використовуєте ви компілятор C або C ++, ніяке керування іменами не буде здійснено для імен у extern "C"блоці.
Джонатан Векі

7
"який краще" - напевне, перший варіант кращий: він дозволяє включати заголовок безпосередньо, без будь-яких подальших вимог, як у C, так і в C ++ коді. Другий підхід є вирішенням заголовків C, автор забув про захисти C ++ (проте жодна проблема, якщо вони будуть додані згодом, приймаються вкладені зовнішні "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використання

Речі, які не працюють у зовнішній С

Стає очевидним, що будь-яка функція 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 має лише один можливий некерований символ, тому додаткової роботи не потрібно.

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 ++? :-)
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

205

У кожній програмі C ++ всі нестатичні функції представлені у двійковому файлі як символи. Ці символи - це спеціальні текстові рядки, які однозначно ідентифікують функцію в програмі.

У C ім'я символу те саме, що ім'я функції. Це можливо тому, що в C жодна дві нестатичні функції не можуть мати однакову назву.

Оскільки C ++ дозволяє перевантажувати і має безліч функцій, яким C не подобається класів, функцій членів, специфікацій винятків - не можна просто використовувати ім'я функції як ім'я символу. Щоб вирішити це, C ++ використовує так зване ім'я mangling, яке перетворює ім'я функції та всю необхідну інформацію (наприклад, кількість та розмір аргументів) у якусь дивну рядок, оброблений лише компілятором та лінкером.

Отже, якщо ви вказали функцію, яка буде зовнішньою C, компілятор не виконує керування іменем, і до неї можна отримати прямий доступ, використовуючи його ім'я символу як ім'я функції.

Це стане в нагоді під час використання dlsym()та dlopen()виклику таких функцій.


що ти маєш на увазі під рукою? це символ name = ім'я функції зробить ім'я символу переданим dlsym відомим, чи інше?
Помилка

1
@Error: так. По суті, неможливо в загальному випадку 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;
}

Компілятор змінного струму не буде компілювати вищевказаний приклад, оскільки одна і та ж функція 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 ++ не маніпулювати ім'ям функції.

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

Щось має сенс, але це щось, про що слід пам’ятати, коли переносите C-код на C ++.


14
extern "C"означає використовувати зв'язок C, як описано в інших відповідях. Це не означає "компілювати вміст як C" або щось інше. int virtual;недійсний у C ++, і якщо вказати різні зв’язки, це не змінить.
ММ

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

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

28

Це змінює зв'язок функції таким чином, що функцію можна викликати від C. На практиці це означає, що ім'я функції не є керованим .


3
Мангл - це загальновживаний термін ... Не вірте, я ніколи не бачив "прикрашених", що вживаються з цим значенням.
Меттью Шарлі

1
Microsoft (принаймні частково) використовує в своїй документації декоровані, а не підроблені. вони навіть називають свій інструмент, щоб підкреслити (інакше, невміло) ім'я undname.
Рене Ніффенеггер

20

Він повідомляє компілятор C ++ шукати назви цих функцій у стилі С під час з'єднання, оскільки назви функцій, складених на C та C ++, відрізняються під час етапу зв’язування.


12

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


6

Я раніше використовував "extern" C "'для файлів dll (динамічна бібліотека посилань), щоб зробити etc. 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.
Неочікуваний

1
Це лише повторилося, що ви не знаєте, чим займаєтесь, але це робиться таким чином, як для вас, для деяких нерозкритих списків цільових платформ. Ви не зверталися до проблем, про які я порушив у своєму попередньому коментарі. Це все ще є голосування під голосом, оскільки якраз помиляюся (є більше, що не вмістилося в жодному коментарі).
Неочікуваний

1
Опублікування відповіді на Stack Overflow означає, що ви знаєте, що робите. Це очікується. Що стосується вашої спроби "запобігти пошкодженню стека під час запуску" : ваш підпис функції вказує повернене значення типу void*, але ваша реалізація нічого не повертає. Це полетить справді добре ...
Неочікуваний

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 тощо). Рішення цих проблем - це вони перетворюються на різні рядки (називаються символи ), символи обліковують назву функції, також аргументи, тому кожну з цих функцій, навіть з тим самим іменем, можна однозначно ідентифікувати (також називається, ім'я mangling)
  • у C у вас немає перевантаження, ім'я функції унікальне (значить, окрема рядок для однозначного визначення імені функції не потрібна, тому символ - це саме ім'я функції)

Так
у C ++, з ім'ям mangling однозначно ідентифікує кожну функцію
в C, навіть без імені mangling однозначно ідентифікує кожну функцію

Щоб змінити поведінку C ++, тобто вказати, що керування іменами не повинно відбуватися для певної функції, ви можете використовувати зовнішнє "C" перед назвою функції з будь-якої причини, наприклад, експортуючи функцію з певним іменем з dll , для використання його клієнтами.

Прочитайте інші відповіді, щоб отримати детальніші / правильніші відповіді.


1

При змішуванні C і C ++ (тобто, виклику функції C з C ++; і b. Виклику функції C ++ від C), керування іменем C ++ викликає проблеми з пов’язанням. З технічної точки зору ця проблема виникає лише тоді, коли функції виклику вже були зібрані у двійковий (швидше за все, файл * .a бібліотеки) за допомогою відповідного компілятора.

Тому нам потрібно використовувати зовнішній "C", щоб відключити керування іменами в C ++.


0

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

Що саме робить компілятор C ++ : він маніпулює іменами в процесі компіляції, отже, нам потрібно сказати компілятору, щоб лікувати C спеціально реалізації.

Коли ми створюємо класи C ++ і додаємо extern "C", ми повідомляємо нашому компілятору C ++, що ми використовуємо конвенцію для виклику C.

Причина (ми викликаємо реалізацію C від C ++): або ми хочемо викликати функцію C з C ++, або викликати функцію C ++ з C (класи C ++ ... тощо не працюють у C).


Ласкаво просимо до переповнення стека. Якщо ви вирішите відповісти на старіші запитання, на які є чітко встановлені та правильні відповіді, додавання нової відповіді в кінці дня може не отримати вам жодної заслуги. Якщо у вас є якась нова відмінна інформація, або ви впевнені, що інші відповіді невірні, будь-ласка, додайте нову відповідь, але "ще одна відповідь", що дає ту саму основну інформацію довгий час після того, як запитання, як правило, вигравали " t заробляєш багато кредиту. Чесно кажучи, я не думаю, що у цій відповіді є щось нове.
Джонатан Леффлер

Ну, я мав би пам’ятати вашу думку - гаразд
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.