Языковая связь - это термин, используемый для связи между фрагментами кода C++
и non-C++
.Как правило, в программе на C ++ все имена функций, типы функций и даже имена переменных имеют связь по умолчанию с языком C ++.
Объектный код C ++ может быть связан с другим объектным кодом, который создается с использованием некоторого другого исходного языка (как C
) с использованием предопределенного спецификатора связи.
Поскольку вам необходимо знать концепцию name mangling
, которая кодирует имена функций, типы функций и имена переменных, чтобы сгенерировать для них уникальное имя.Это позволяет компоновщику различать общие имена (как в случае перегрузки функций).Изменение имени нежелательно при связывании модулей C с библиотеками или объектными файлами, скомпилированными с помощью компилятора C ++.Для предотвращения искажения имени в таких случаях используются спецификаторы связи.В этом случае extern "C"
является спецификатором связи.Давайте рассмотрим пример (код c ++ упоминает здесь ):
typedef int (*pfun)(int); // line 1
extern "C" void foo(pfun); // line 2
extern "C" int g(int) // line 3
...
foo( g ); // Error! // line 5
В строке 1 объявляется pfun
для указания на функцию C ++, поскольку в ней отсутствует спецификатор связи.
Строка 2 поэтому объявляет, что foo является функцией C, которая принимает указатель на функцию C ++.
Строка 5 пытается вызвать foo с указателем на g, функцией C, несовпадением типов.
Различия в связывании имени функции:
Давайте возьмем два разных файла:
Один с extern "c"
связью (file1.cpp):
#include <iostream>
using namespace std;
extern "C"
{
void foo (int a, int b)
{
cout << "here";
}
}
int main ()
{
foo (10,20);
return 0;
}
Один без extern "c"
linkage (file2.cpp):
#include <iostream>
using namespace std;
void foo (int a, int b)
{
cout << "here";
}
int main ()
{
foo (10,20);
return 0;
}
Теперь скомпилируйте эти два и проверьте objdump.
# g++ file1.cpp -o file1
# objdump -Dx file1
# g++ file2.cpp -o file2
# objdump -Dx file2
С внешней связью "C" нет именикалеча для функции foo
.Таким образом, любая программа, которая ее использует (при условии, что мы делаем из нее разделяемую библиотеку), может напрямую вызывать foo (с помощью вспомогательных функций, таких как dlsym
и dlopen
) без учета каких-либо эффектов искажения имен.1042 * С другой стороны, когда не используется extern "C"
, func: foo
искажается некоторыми предопределенными правилами (известными используемому компилятору / компоновщику), и поэтому приложение не может напрямую вызывать его из него, указав имякак foo
.Вы можете, однако, назвать его искаженным именем (в данном случае _Z3fooii
), но никто не использует его по очевидной причине.
0000000000400774 <_Z3fooii>:
400774: 55 push %rbp
400775: 48 89 e5 mov %rsp,%rbp
...
...
400791: c9 leaveq
400792: c3 retq
0000000000400793 <main>:
400793: 55 push %rbp
400794: 48 89 e5 mov %rsp,%rbp
400797: be 14 00 00 00 mov $0x14,%esi
40079c: bf 0a 00 00 00 mov $0xa,%edi
4007a1: e8 ce ff ff ff callq 400774 <_Z3fooii>
4007a6: b8 00 00 00 00 mov $0x0,%eax
4007ab: c9 leaveq
4007ac: c3 retq
Эта страница также являетсяХорошее чтение для этой конкретной темы.
Хорошая и четко объясненная статья о соглашении о вызовах: http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx