Как работает внешняя декларация "C"? - PullRequest
26 голосов
/ 08 марта 2010

Я прохожу курс обучения языкам программирования, и мы говорим о декларации extern "C".

Как это объявление работает на более глубоком уровне, кроме "оно взаимодействует с C и C ++"? Как это влияет на привязки, которые также имеют место в программе?

Ответы [ 9 ]

46 голосов
/ 08 марта 2010

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


Пример:

Допустим, у нас есть следующий код в файле с именем test.cpp:

extern "C" {
  int foo() {
    return 1;
  }
}

int bar() {
  return 1;
}

Если вы запустите gcc -c test.cpp -o test.o

Взгляните на названия символов:

00000010 T _Z3barv

00000000 T foo

foo() сохраняет свое имя.

25 голосов
/ 08 марта 2010

Давайте рассмотрим типичную функцию, которая может компилироваться как в C, так и в C ++:

int Add (int a, int b)
{
    return a+b;
}

Теперь в C функция называется внутренне "_Add". Принимая во внимание, что функция C ++ внутренне называется чем-то совершенно другим, используя систему, называемую mangling. Это в основном способ назвать функцию так, чтобы одна и та же функция с разными параметрами имела другое внутреннее имя.

Так что, если Add () определен в add.c, а у вас есть прототип в add.h, у вас возникнет проблема, если вы попытаетесь включить add.h в файл C ++. Поскольку код C ++ ищет функцию с именем, отличным от имени в add.c, вы получите ошибку компоновщика. Чтобы обойти эту проблему, вы должны добавить add.c следующим способом:

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

Теперь код C ++ будет связываться с _Add вместо искаженной версии имени C ++.

Это одно из употреблений выражения. В итоге, если вам нужно скомпилировать код, строго на C, в программе на C ++ (с помощью оператора include или каким-либо другим способом), вам нужно обернуть его внешним объявлением "C" {...}.

9 голосов
/ 08 марта 2010

Когда вы помечаете блок кода с помощью extern "C", вы говорите системе использовать связь в стиле C.

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

5 голосов
/ 09 марта 2010

Следует отметить, что extern "C" также изменяет типы функций. Он не только изменяет вещи на более низких уровнях:

extern "C" typedef void (*function_ptr_t)();

void foo();

int main() { function_ptr_t fptr = &foo; } // error!

Тип &foo не равен типу, который определяет typedef (хотя код принят некоторыми, но не всеми компиляторами).

5 голосов
/ 08 марта 2010

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

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

4 голосов
/ 08 марта 2010

extern C влияет на искажение имени компилятором C ++. Это способ заставить компилятор C ++ не искажать имена, или, скорее, манипулировать ими так же, как это делает компилятор C. Это способ взаимодействия C и C ++.

Как пример:

extern "C" void foo(int i);

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

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

Так что вам нужно использовать это:

#ifdef __cplusplus
extern "C" {
#endif

void foo(int i);

#ifdef __cplusplus
}
#endif

Теперь, когда это появляется в заголовочном файле, компиляторы C и C ++ будут довольны объявлением, и теперь оно может быть определено в модуле C или C ++ и может вызываться как кодом C, так и C ++. 1014 *

3 голосов
/ 08 марта 2010

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

http://en.wikipedia.org/wiki/Name_mangling

int example(int alpha, char beta);

в С: _example

в C ++: __Z7exampleic

Обновление: как GManNickG отмечает в комментариях, шаблон искажения имени зависит от компилятора.

0 голосов
/ 30 апреля 2015

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

Например, фрагмент кода выглядит следующим образом:

int _cdecl func1(void) {return 0}
int _stdcall func2(int) {return 0}
int _fastcall func3(void) {return 1}

32-битные C-компиляторы переведут код в следующую форму:

_func1
_func2@4
@func3@4

в cdecl, func1 будет переводиться как ' _name '

в stdcall, func2 будет переводиться как ' _name @ X '

в fastcall func2 будет переводиться как ' @ name @ X '

' X ' означает количество байтов параметров в списке параметров.

64-битное соглашение в Windows не имеет начального подчеркивания

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

например, фрагмент кода выглядит следующим образом:

int func(void) {return 1;}
int func(int) {return 0;}
int func_call(void) {int m=func(), n=func(0);}

Компилятор C ++ переведет код следующим образом:

int func_v(void) {return 1;}
int func_i(int) {return 0;}
int func_call(void) {int m=_func_v(), n=_func_i(0);}

'_ v' и '_i' представляют собой информацию о типах 'void' и 'int'

0 голосов
/ 08 марта 2010

Вот цитата из MSDN

"Ключевое слово extern объявляет переменную или функцию и указывает, что она имеет внешнюю связь (ее имя видно из файлов, отличных от того, в котором она определена). При изменении переменной extern указывает, что переменная имеет статическую продолжительность ( он выделяется, когда программа начинается, и освобождается, когда программа заканчивается). Переменная или функция могут быть определены в другом исходном файле или позже в том же файле. По умолчанию объявления переменных и функций в области видимости файла являются внешними. "

http://msdn.microsoft.com/en-us/library/0603949d%28VS.80%29.aspx

...