Co oznacza, że nazwa lub typ ma określony związek językowy?

Zgodnie z (c) ANSI ISO/IEC 14882:2003, strona 127:

Dane techniczne gniazda. Gdy specyfikacje połączeń zagnieżdżają się, najbardziej wewnętrzna określa język. Specyfikacja powiązania nie określa zakresu. Specyfikacja powiązania występuje tylko w zakresie przestrzeni nazw (3.3). W specyfikacji linkage, określony język linkage ma zastosowanie do typów funkcji wszystkich deklaratorów funkcji, nazw funkcji i nazw zmiennych wprowadzonych przez deklaracja (- y).

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's type has C language linkage

extern "C" FUNC f3;
// the name of function f3 and the function's 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
Co to wszystko znaczy? Na przykład, jakie powiązanie ma funkcja f2(), Związek języka C lub C++?

Jak zauważył @ Johannes Schaub, nie ma prawdziwego wyjaśnienia, co to oznacza w standardzie, więc może być różnie interpretowane w różnych kompilatorach.

Proszę wyjaśnić różnice w pliku obiektowym:

  • nazwa funkcji z połączeniem języka C i języka C++.
  • funkcja Wpisz z linkiem języka C i linkiem języka C++.
Author: Jonathan Leffler, 2011-04-23

7 answers

Powiązanie językowe to termin używany dla powiązania pomiędzy C++ i non-C++ fragmentami kodu. Zazwyczaj w programie C++ Wszystkie nazwy funkcji, typy funkcji, a nawet nazwy zmiennych mają domyślne powiązanie języka C++.

Kod obiektowy C++ może być połączony z innym kodem obiektowym, który jest wytwarzany przy użyciu innego języka źródłowego (np.

Jak trzeba mieć świadomość pojęcia name mangling, które koduje nazwy funkcji, typy funkcji i nazwy zmiennych tak, aby wygenerować dla nich unikalną nazwę. Pozwala to linkerowi na rozróżnienie nazw zwyczajowych (jak w przypadku przeciążenia funkcji). Manglowanie nazw nie jest pożądane przy łączeniu modułów C z bibliotekami lub plikami obiektowymi skompilowanymi za pomocą kompilatora C++. Aby zapobiec zniekształcaniu nazw w takich przypadkach, stosuje się specyfikatory powiązań. W tym przypadku {[10] } jest określnikiem powiązania. Weźmy przykład (kod c++ wymieniony tutaj):

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

Linia 1 pfun wskazywać na funkcję C++, ponieważ brakuje w niej specyfika linkowania.

Linia 2 deklaruje zatem foo jako funkcję C, która przyjmuje wskaźnik do funkcji C++.

Linia 5 próbuje wywołać foo ze wskaźnikiem do G, funkcji C, typu miss-match.

Różnica w linkowaniu nazwy funkcji:

Weźmy dwa różne pliki:

Jeden z 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;
}

One without 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;
}

Teraz skompiluj te dwa i sprawdź objdump.

# g++ file1.cpp -o file1
# objdump -Dx file1

# g++ file2.cpp -o file2
# objdump -Dx file2

W przypadku połączenia extern " C " nie ma nazwy dla funkcji foo. Tak więc każdy program, który go używa (zakładając, że tworzymy z niego współdzieloną bibliotekę) może bezpośrednio wywoływać foo (z funkcjami pomocniczymi, takimi jak dlsym i dlopen) bez uwzględniania jakichkolwiek efektów manipulowania nazwami.

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 

Z drugiej strony, gdy nie jest używane extern "C", func: foo jest zniekształcony z pewnymi predefiniowanymi regułami (znanymi dla kompilatora/linkera) i tak aplikacja może nie wywołaj go bezpośrednio od niego, podając nazwę jako foo. Można go jednak nazwać zniekształconą nazwą (_Z3fooii w tym przypadku), jeśli chcesz, ale nikt nie używa go z oczywistego powodu.

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   

Ta strona jest również dobrą lekturą dla tego konkretnego tematu.

Ładny i jasno wyjaśniony artykuł o wywoływaniu konwencji: http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

 16
Author: Vikram.exe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-26 01:28:50

" Nazwa f2 ma związek z językiem C++ " W języku C++ linkage definiuje go nie tylko nazwa funkcji, ale także rodzaj jej argumentów i wartość zwracana. w tym przypadku masz: void f2 (void); ale można zdefiniować z nim: void f2 (int a); bez konfliktu, ponieważ linkage będzie je postrzegać jako różne typy, co nie byłoby w stanie zrobić w języku C.

" typ funkcji ma powiązania w języku C" Nie znam szczegółów, ale znam ich wysoki poziom. Zasadniczo sprawia, że skompilowana funkcja C++ może być łączona z C. Jeśli dobrze pamiętam w C i C++ sposób przekazywania parametrów do funkcji jest inny. W tym przypadku funkcja f2 przekaże parametry tak jak robi to kompilator C. w ten sposób funkcja będzie łączona zarówno z C jak i C++.

 2
Author: Roee Gavirel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-18 08:22:41
extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the
// function's type has C language linkage

Nazwa FUNC jest zadeklarowana przez "C", ponieważ mówi extern "C" w pierwszej linii.

Nazwa f2 ma łącze C++, ponieważ jest to domyślne, a żadne inne łącze nie jest podane w drugiej linii.

Fakt, że nazwa f2 jest używana w odniesieniu do funkcji z powiązaniem C, nie zmienia powiązania nazwy .

 2
Author: Bo Persson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-19 15:21:40

Ma to związek z ABI (Application Binary Interface) programu.

Ponieważ API określa zewnętrzny interfejs kodu źródłowegoprogramu, ABI określa zewnętrzny interfejs kodu binarnego programu (skompilowanej wersji).


Pierwotnie funkcje C miały po prostu kilka różnych form. Coś jak

int foo(int);

Będzie poprzedzone podkreśleniem przez kompilator, tworząc _foo, a następnie eksportowane w celu udostępnienia innym aplikacjom.

To jednak nie wystarczyło. Jeśli spojrzysz na API systemu Windows, na przykład, zobaczysz takie rzeczy jak:

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

Dzieje się tak dlatego, że nie ma sposobu na rozróżnienie przeciążeń funkcji po prostu patrząc na nazwę funkcji, więc ludzie zaczęli je zmieniać, dodając przyrostek Ex (lub podobne).

To było dość brzydkie, i nadal nie pozwalało na przeciążenie operatora, co został napisany w C++. Z tego powodu C++ wymyślił nazwa mangling, aby umieścić dodatkowe informacje w nazwie funkcji, takie jak typy danych jej parametrów, i uczynić ją czymś tajemniczym z dużą ilością @ symboli.

Wszystko było dobrze, z wyjątkiem tego, że nie było całkowicie ustandaryzowane .

Oczywiście, gdy powstawały nowe języki i kompilatory, każdy z nich miał swój własny schemat, niektóre niezgodne z innymi. Więc jeśli potrzebujesz Importuj lub Eksportuj zewnętrzną funkcję, musisz określić, jakiego rodzaju ABI powinien szukać kompilator, stąd extern "C++" masz tam.

 2
Author: Mehrdad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-19 19:20:10
Co to wszystko znaczy? Na przykład, jakie powiązanie ma funkcja f2 (), związek języka C lub C++?
extern "C" typedef void FUNC();
FUNC f2;
// the name f2 has C++ language linkage and the 
// function's type has C language linkage 

To, co nazywasz funkcją "F2 ()" ma dwa aspekty powiązania:

  • wymazywanie lub nie jego nazwy w tabeli symboli (która ma powiązania w języku C++), oraz
  • konieczna jest konwencja wywołania C lub C++, jeśli funkcja zostanie wywołana (C).

Aby wywołać f2() znajdujesz jego nazwę aka symbol w pliku obiektowym, która będzie zniekształconą wersją "funkcji o nazwie f2 nie pobierającej argumentów". Możesz to banalnie zweryfikować kompilując powyższy kod i sprawdzając obiekt(np. w / GNU tools nm --demangle).

Ale aby wywołać funkcję, konwencje dla pre-i post-conditions ponownego użycia rejestru, ustawienia stosu itp. są te z funkcji C. Jest legalne, aby funkcje C i C++ miały różne konwencje wywołania i może być zrobione - na przykład-w celu ułatwienia obsługi wyjątków w C++.

Proszę wyjaśnić różnice w pliku obiektowym: nazwa funkcji z powiązaniem języka C i powiązaniem języka C++.

  • dla połączenia C, " f2 " będzie symbolem w pliku obiektowym wynikającym z f2()
  • W przypadku linkage ' U C++, niektóre zniekształcone wersje "funkcji o nazwie f2 nie pobierającej argumentów" (dla GNU, _Z2f2v, która demangle do f2())

Typ funkcji z powiązaniem języka C i powiązaniem języka C++.

Jak wspomniano powyżej, chodzi o konwencję użycia rejestru/stosu do wywołania kodu pod adresem funkcji. Ta meta-informacja niekoniecznie jest przechowywana w informacji o tabeli symboli obiektu (i na pewno nie jest częścią klucza nazwy symbolu).

Ponadto, ponieważ każda funkcja przyjmuje jedną z Konwencji wywołujących, kompilator musi znać konwencję wywołującą, której należy użyć, gdy podąża za wskaźnikiem do funkcji: z tym wglądem, myślę, że pozostały kod w pytaniu staje się czysto.

Jest doskonała dyskusja na http://developers.sun.com/solaris/articles/mixing.html - w szczególności polecam sekcję pracującą ze wskaźnikami do funkcji .

 2
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-20 03:06:27

Jak wszyscy wiemy w C/C++ tłumaczenie kodu składa się z dwóch głównych faz: kompilacji i linkowania. Gdy kompilator generuje pliki obiektowe, przekazuje do linkera informacje określające, w których plikach obiektowych dana funkcja jest wywoływana lub odwoływana. W C jest tak, że funkcja ma nazwę i definicję pasującą.

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

I po kompilacji pliku1.obj przechowuje kod i informacje o definicji symbolu foo.

Ale gdy C++ pojawia się w nazwach symboli stać się bardziej skomplikowane. Funkcja może być przeciążona lub być członkiem klasy. Ale łącznik nie chce tego wiedzieć. Aby zachować prostotę i ponowne użycie starszych linkerów, potrzebuje jednej nazwy, niezależnie od tego, czy foo to:

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

Ale nie można go już nazywać tylko foo, więc nadchodzi nazwa mangling. I możemy uzyskać z kompilatora pewne odmiany, takie jak foo_void, foo_int, foo_void_classa. I wreszcie łącznik jest szczęśliwy, ponieważ wszystkie te wyglądają na proste symbole.

Kiedy chcemy aby wywołać funkcję foo skompilowaną z kompilatorem C w kodzie C++, musimy powiedzieć kompilatorowi, że chcemy, aby foo było w stylu C foo, a nie fo_void, jak mógłby zakładać kompilator C++. Odbywa się to za pomocą:

extern "C" void foo();

Teraz kompilator wie, że foo jest kompilowane przy użyciu kompilatora C i przekaże linkerowi informacje, które ten kod nazywa foo. Linker dopasuje go do definicji foo w pliku1.obj. Więc to wszystko, co myślę.

Niektóre inne dyrektywy, takie jak cdecl lub stdcall, są specyficzne dla systemu Windows i powiedzieć, jak parametry w wywołaniach funkcji są przekazywane. Tak, dla C i C++ jest to cdecl. Jednak funkcje Windows API wykorzystują konwencję stdcall-Pascal (prostota i historycznie Microsoft udostępnił kiedyś środowisko programistyczne Windows w Pascalu).

 1
Author: jszpilewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-19 15:03:09

Każda funkcja, typ funkcji i obiekt ma powiązanie językowe, które jest określone jako prosty ciąg znaków. Domyślnie łącze jest " C++". Jedynym standardowym połączeniem językowym jest "C". Wszystkie inne powiązania językowe i właściwości związane z różnymi powiązania językowe są zdefiniowane w implementacji.

 0
Author: Community,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-10 23:13:46