Подтвердить что ты не робот

Что означает, что имя или тип имеют определенную языковую связь?

Согласно (c) ANSI ISO/IEC 14882: 2003, стр. 127:

Спецификация привязки привязки. Когда спецификация привязки привязки, самая внутренняя определяет язык. Спецификация связи не устанавливает область действия. Спецификация привязки должна выполняться только в области пространства имен (3.3). В спецификации привязки указанная языковая связь применяется к типам функций всех деклараторов функций, имен функций и имен переменных, введенных декларациями (объявлениями).

extern "C" void f1(void(*pf)(int));
// the name f1 and its function type have C language
// linkage; pf is a pointer to a C function

extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the
// function type has C language linkage

extern "C" FUNC f3;
// the name of function f3 and the function type
// have C language linkage

void (*pf2)(FUNC*);
// the name of the variable pf2 has C++ linkage and
// the type of pf2 is pointer to C++ function that
// takes one parameter of type pointer to C function

Что все это значит? Например, какая связь имеет функция f2(), связанная с языком C или С++?

Как отметил @Johannes Schaub, нет реального объяснения того, что это означает в стандарте, поэтому его можно интерпретировать по-разному в разных компиляторах.

Пожалуйста, объясните различия в объектном файле:

  • имя функции с привязкой языка C и связью языка С++.
  • тип функции с C-языковой связью и связью языка С++.
4b9b3361

Ответ 1

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

Объектный код С++ может быть связан с другим объектным кодом, который создается с использованием некоторого другого исходного языка (например, C) с использованием предопределенного спецификатора привязки.

Как вы должны знать концепцию name mangling, которая кодирует имена функций, типы функций и имена переменных, чтобы генерировать для них уникальное имя. Это позволяет компоновщику различать общие имена (как в случае перегрузки функций). Манипуляция имени нежелательна при связывании модулей C с библиотеками или объектными файлами, скомпилированными с помощью компилятора С++. Чтобы предотвратить манипулирование именами для таких случаев, используются спецификаторы привязки. В этом случае extern "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 для указания функции С++, поскольку ей не хватает спецификатора привязки.

Таким образом, строка 2 объявляет foo как функцию C, которая принимает указатель на функцию С++.

Строка 5 пытается вызвать foo с указателем на g, функцией C, неправильным совпадением типа.

Diff в имени имени функции:

Возьмем два разных файла:

Один с extern "C" linkage (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), без учета каких-либо эффектов смены имени.

0000000000400774 <foo>:
  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 <foo>
  4007a6:   b8 00 00 00 00          mov    $0x0,%eax
  4007ab:   c9                      leaveq 

С другой стороны, когда no 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

Ответ 2

"имя f2 имеет ссылку на С++ язык" В языке С++ ссылка определяет не только имя функции, но и тип ее аргументов и возвращаемое значение. в этом случае у вас есть: void f2 (void); но вы можете определить с ним: void f2 (int a); без конфликта, потому что связь увидит их как разные типы, что вы не сможете сделать на языке C.

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

Ответ 3

extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the
// function type has C language linkage

Имя FUNC объявляется связью "C", потому что в первой строке указано extern "C".

Имя f2 имеет ссылку на С++, потому что это значение по умолчанию, и никакая другая ссылка не указана в строке 2.

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

Ответ 4

Он связан с ABI (Application Binary Interface) программы.

Поскольку API определяет внешний интерфейс исходного кода программы, ABI указывает внешний интерфейс двоичного кода программы (скомпилированной версии).


Первоначально функции C просто имели несколько разных форм. Что-то вроде

int foo(int);

будет префиксным символом подчеркивания компилятором, чтобы сформировать _foo, а затем экспортировать, чтобы он был доступен для других приложений.

Однако этого было недостаточно. Если вы посмотрите на Windows API, например, вы увидите такие вещи, как:

DWORD CreateWindowW(...);        //Original parameters
DWORD CreateWindowExW(..., ...); //More parameters

Это связано с тем, что нет возможности различать перегрузки функции просто, глядя на имя функции, поэтому люди начали изменять их, добавив суффикс Ex (или тому подобное).

Это стало довольно уродливым, и это все еще не позволяло перегрузке оператора, которая была показана на С++. Из-за этого С++ придумал name mangling, чтобы добавить дополнительную информацию в имя функции, например типы данных ее параметров, и делая его чем-то загадочным с большим количеством символов @.

Все было хорошо, за исключением того, что он не был полностью стандартизован.

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

Ответ 5

Что все это значит? Например, какая связь имеет функция f2(), связь языка C или С++?

extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the 
// function type has C language linkage 

То, что вы называете функцией f2(), имеет два аспекта его привязки:

  • изменение или отсутствие его имени в таблице символов (имеющей связь языка С++) и
  • требуется вызов C или С++ для вызова функции (C).

Чтобы вызвать f2(), вы найдете его имя aka в объектном файле, которое будет искаженной версией "function с именем f2 без аргументов". Вы можете проверить это тривиально, скомпилировав вышеуказанный код и проверив объект (например, w/GNU tools nm --demangle).

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

Пожалуйста, объясните различия в объектном файле: имя функции с C-языковой связью и связью языка С++.

  • для ссылки C, "f2" будет символом в объектном файле, полученном в результате f2()
  • для С++ linkage, некоторая измененная версия функции с именем f2 без аргументов (для GNU, _Z2f2v, которая соединяется с f2())

тип функции с C-языковой связью и связью языка С++.

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

Кроме того, поскольку каждая функция принимает одно из вызывающих соглашений, компилятор должен знать соглашение о вызове, которое следует использовать при указании на функцию: с этим пониманием я думаю, что оставшийся код в вопросе становится ясным.

Отличное обсуждение в http://developers.sun.com/solaris/articles/mixing.html - в частности, я рекомендую раздел "Работа с указателями на функции".

Ответ 6

Как мы все знаем в C/С++, перевод кода состоит из двух основных этапов: компиляции и компоновки. Когда компилятор генерирует объектные файлы, он передает информацию указанному компоновщику, в котором вызываются или ссылаются объектные файлы с заданной функцией. В C это точно так же, функция имеет имя и соответствующее определение.

// file1.c
void foo(void) {}

И после компиляции file1.obj хранит код и информацию об определении символа foo.

Но когда С++ входит в имена символов, становится более сложным. Функция может быть перегружена или быть членом класса. Но линкер не хочет этого знать. Чтобы сохранить простоту и повторное использование старых компоновщиков, нужно одно имя: foo:

void foo(void) {}
void foo(int) {}
void ClassA::foo(void) {}

Но его больше нельзя назвать просто foo, поэтому здесь происходит сбой имени. И мы можем получить от компилятора некоторые варианты, такие как foo_void, foo_int, foo_void_classa. И, наконец, компоновщик счастлив, поскольку все они смотрят на него как простые символы.

Когда мы хотим вызвать функцию foo, скомпилированную с компилятором C в коде С++, мы должны сообщить компилятору, что мы хотим, чтобы foo был стилем стиля C, а не foo_void, как может предположить компилятор С++. Это делается с помощью:

extern "C" void foo();

Теперь компилятор знает, что foo скомпилирован с использованием компилятора C и передаст информацию компоновщику, который этот код вызывает foo. Компилятор будет соответствовать ему с определением foo в файле file1.obj. Так что все, что я думаю.

Некоторые другие директивы, такие как cdecl или stdcall, являются специфичными для Windows и рассказывают, как передаются параметры в вызовах функций. Да, для C и С++ это cdecl. Но функции Windows API используют соглашение stdcall - Pascal (простота и исторически Microsoft однажды предоставила среду Windows dev в Паскале).

Ответ 7

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