Как работает объявление extern C?

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

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


person samoz    schedule 08.03.2010    source источник


Ответы (9)


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


Пример:

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

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

int bar() {
  return 1;
}

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

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

00000010 Т _Z3barv

00000000 T foo

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

person Bertrand Marron    schedule 08.03.2010
comment
Также эти функции используют соглашение о вызовах C, если оно отличается от соглашения о вызовах C++. Что, вероятно, не так, для функций C++, которые принимают только типы из C в качестве параметров, но с extern "C" это определенно не так. - person Steve Jessop; 08.03.2010
comment
Какой вызов вы использовали, чтобы получить имена символов? - person meawoppl; 01.04.2014

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

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

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

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

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

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

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

person Cthutu    schedule 08.03.2010
comment
Ведущий _ зависит от компилятора/компоновщика/системы. - person Carl Norum; 08.03.2010
comment
Хорошо, но я никогда не сталкивался с компилятором C, который этого не делает, даже со времен Amiga :) - person Cthutu; 09.03.2010
comment
этот ответ помогает устранить ошибки компоновщика, говорящие о неразрешенных внешних символах, в то время как другие ответы дают некоторую подробную концепцию. - person Mohamed Iqzas; 20.10.2014

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

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

person Reed Copsey    schedule 08.03.2010
comment
Я не думаю, что вам следует предлагать, чтобы метод мог быть помечен extern "C". Одиночные функции или несколько функций в порядке. - person quamrana; 08.03.2010
comment
@Reed: эээ .. Я только что понял, что вы говорите о связях и линкерах. Это влияет на именование компилятора, которое лишь косвенно влияет на компоновщик. - person quamrana; 08.03.2010
comment
@quamrana: прочитайте msdn.microsoft.com/en-us /library/0603949d(VS.80).aspx — напрямую влияет на компоновщик и используется им. Компилятор искажает имя, но компоновщик - это тот, кто ИСПОЛЬЗУЕТ именование - extern C существует для того, чтобы позволить компоновщику работать правильно, будь то в C или C++, поскольку компоновщик - это тот, кто должен определить, как отображать между единицами перевода. - person Reed Copsey; 08.03.2010

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

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

void foo();

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

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

person Johannes Schaub - litb    schedule 08.03.2010

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

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

person gilbertc    schedule 08.03.2010
comment
подробнее об изменении имени можно узнать в Википедии: en.wikipedia.org/wiki/Name_mangling - person jakebman; 08.03.2010
comment
' extern C указывает компилятору не использовать соглашение C' Нет, это наоборот. extern C указывает компилятору C++ использовать соглашение C для именования символов. - person JonN; 03.11.2017

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

person quamrana    schedule 08.03.2010

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

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

int example(int alpha, char beta);

in C: _example

in C++: __Z7exampleic

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

person Harvey    schedule 08.03.2010
comment
Имейте в виду, что нет единого искажения имени, все зависит от реализации. - person GManNickG; 08.03.2010

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'

в стандартном вызове func2 будет переведено как '_name@X'

в быстром вызове 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'

person Community    schedule 30.04.2015

Вот цитата из msdn

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

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

person Joe Pitz    schedule 08.03.2010
comment
Это цитата из Microsoft, И так, как она использовалась в течение многих лет, Вы собираетесь позвонить мне с цитатой из Microsoft, я предлагаю вам пойти на класс C - person Joe Pitz; 08.03.2010
comment
Правильно, Microsoft, эти всемирно известные пионеры понятной документации. Эта цитата технически верна, но бесполезна для новичка. - person Samir Talwar; 08.03.2010
comment
Цитата из Microsoft не делает ее актуальной для рассматриваемого вопроса. Речь идет о внешнем в других контекстах. - person Steve Fallows; 08.03.2010
comment
Это гораздо понятнее, чем приведенные выше определения для новичка, чем знакомить новичка с коверканием. Если это технически правильно, то почему звон??? - person Joe Pitz; 08.03.2010
comment
@ Джо, потому что это не ответ на вопрос. extern сами по себе и extern "C" делают разные вещи. - person Carl Norum; 08.03.2010
comment
Там ничего не сказано о части C. Вы не можете объяснить extern c, не упомянув искажения. - person Steve Fallows; 08.03.2010
comment
На самом деле я хотел спросить об этом. Я неправильно понял вопрос о extern в C как о extern C. - person samoz; 08.03.2010