Зачем нам нужен extern «C» {#include } в C ++?

133
задан sepp2k 8 March 2019 в 20:56
поделиться

10 ответов

C и C++ поверхностно подобны, но каждый компилирует в совсем другой набор кода. При включении заголовочного файла с компилятором C++ компилятор ожидает код C++. Если, однако, это будет заголовок C, то компилятор ожидает, что данные, содержавшиеся в заголовочном файле, будут скомпилированы в определенный формат — C++ 'ABI' или 'Двоичный интерфейс приложений', таким образом, компоновщик засорит. Это предпочтительно для передачи данных C++ к функции, ожидая C данные.

(Для вхождения действительно основные элементы ABI C++ обычно 'искажает' названия их функций/методов, таким образом звоня printf() не отмечая прототип как функция C, C++ на самом деле генерирует вызов кода _Zprintf, плюс дополнительное дерьмо в конце.)

Так: использовать extern "C" {...} когда включая c заголовок — это настолько просто. Иначе у Вас будет несоответствие в скомпилированном коде, и компоновщик будет дросселировать. Для большинства заголовков, однако, Вам даже не будет нужно extern потому что большая часть системы C заголовки будет уже составлять то, что они могли бы быть включены кодом C++ и уже extern их код.

121
ответ дан 24 November 2019 в 00:00
поделиться

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

Учитывая тест. C как так:

void foo() { }

Компиляция и список символов в объектном файле дают:

$ g++ -c test.C
$ nm test.o
0000000000000000 T _Z3foov
                 U __gxx_personality_v0

Функция нечто на самом деле вызвана "_Z3foov". Эта строка содержит информацию о типе для типа возврата и параметров, среди прочего. Если Вы вместо этого тест записи. C как это:

extern "C" {
    void foo() { }
}

Затем скомпилируйте и посмотрите на символы:

$ g++ -c test.C
$ nm test.o
                 U __gxx_personality_v0
0000000000000000 T foo

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

Вы обычно включаете заголовок в экстерне "C" {}, если код, который идет с ним, был скомпилирован с компилятором C, но Вы пытаетесь назвать его от C++. Когда Вы делаете это, Вы говорите компилятору, что все объявления в заголовке будут использовать связь C. При соединении кода.o файлы будут содержать ссылки на "нечто", не "_Z3fooblah", который, надо надеяться, соответствует тому, что находится в библиотеке, против которой Вы связываетесь.

Большинство современных библиотек поместит защиту вокруг таких заголовков так, чтобы символы были объявлены с правильной связью. например, в большом количестве стандартных заголовков Вы найдете:

#ifdef __cplusplus
extern "C" {
#endif

... declarations ...

#ifdef __cplusplus
}
#endif

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

108
ответ дан 24 November 2019 в 00:00
поделиться

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

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

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

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

21
ответ дан 24 November 2019 в 00:00
поделиться

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

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

14
ответ дан 24 November 2019 в 00:00
поделиться

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

В C правило очень просто, символы - все в едином пространстве имен так или иначе. Так целочисленные "носки" хранится как "носки", и функция count_socks хранится как "count_socks".

Компоновщики были созданы для C и других языков как C с этим простым правилом именования символа. Таким образом, символы в компоновщике являются просто простыми строками.

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

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

11
ответ дан 24 November 2019 в 00:00
поделиться

Когда мы должны использовать его?

Когда Вы связываете C libaries в файлы объекта C++

Что происходит на уровне компилятора/компоновщика, который требует, чтобы мы использовали его?

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

Как с точки зрения компиляции/соединения это решает проблемы, которые требуют, чтобы мы использовали ее?

Используя схему именования C позволяет Вам ссылочным символам C-стиля. Иначе компоновщик попробовал бы символы в стиле С++, которые не будут работать.

11
ответ дан 24 November 2019 в 00:00
поделиться

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

7
ответ дан 24 November 2019 в 00:00
поделиться

Необходимо использовать экстерна "C" в любое время это, Вы включаете заголовок, определяющий функции, находящиеся в файле, скомпилированном компилятором C, используемым в файле C++. (Много стандартных библиотек для C могут включать эту регистрацию их заголовков для создания этого более простым для разработчика),

Например, если у Вас есть проект с 3 файлами, util.c, util.h, и main.cpp и и.c и .cpp файлы компилируются с компилятором C++ (g ++, cc, и т.д.) затем он не действительно необходим и может даже вызвать ошибки компоновщика. Если Ваш процесс сборки будет использовать регулярный компилятор C для util.c, то необходимо будет использовать экстерна "C" когда включая util.h.

То, что происходит, - то, что C++ кодирует параметры функции на ее имя. Это - то, как перегрузка функции работает. Все, что имеет тенденцию происходить с функцией C, является добавлением подчеркивания (" _ ") к началу имени. Не используя экстерна "C" компоновщик будет искать функцию под названием DoSomething @@ int@float (), когда подлинное имя функции будет _DoSomething () или просто DoSomething ().

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

7
ответ дан 24 November 2019 в 00:00
поделиться

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

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

6
ответ дан 24 November 2019 в 00:00
поделиться

Это используется для решения вопросов искажения имени. экстерн C подразумевает, что функции находятся в "плоском" C-стиле API.

5
ответ дан 24 November 2019 в 00:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: