¿Qué significa que un nombre o tipo tenga una cierta vinculación lingüística?


De acuerdo con (c) ANSI ISO / IEC 14882: 2003, página 127:

Las especificaciones de enlace anidan. Cuando se anidan las especificaciones de enlace, la más interna determina el idioma. Una especificación de vinculación no establece un alcance. Una especificación de enlace solo se producirá en el ámbito del espacio de nombres (3.3). En una especificación de enlace, el enlace de lenguaje especificado se aplica a los tipos de función de todos los declaradores de función, nombres de función y nombres de variable introducidos por el declaración(es).

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

¿Qué significa todo esto? Por ejemplo, ¿qué enlace tiene la función f2(), enlace del lenguaje C o C++?

Como señaló @Johannes Schaub, no hay una explicación real de lo que esto significa en el Estándar, por lo que puede interpretarse de manera diferente en diferentes compiladores.

Por favor explique las diferencias en el archivo objeto:

  • nombre de una función con enlace de lenguaje C y enlace de lenguaje C++.
  • una función escriba con enlace de lenguaje C y enlace de lenguaje C++.
Author: Jonathan Leffler, 2011-04-23

7 answers

Language linkage es el término utilizado para el enlace entre C++ y non-C++ fragmentos de código. Normalmente, en un programa C++, todos los nombres de funciones, tipos de funciones e incluso nombres de variables tienen el enlace predeterminado del lenguaje C++.

Un código objeto C++ se puede vincular a otro código objeto que se produce utilizando algún otro lenguaje fuente (como C) utilizando un especificador de enlace predefinido.

Como debe ser consciente del concepto de name mangling, que codifica nombres de funciones, tipos de funciones y nombres de variables para generar un nombre único para ellos. Esto permite al enlazador diferenciar entre nombres comunes (como en el caso de la sobrecarga de funciones). La alteración de nombres no es deseable cuando se vinculan módulos C con bibliotecas o archivos objeto compilados con un compilador C++. Para evitar la alteración de nombres en estos casos, se utilizan especificadores de enlace. En este caso, extern "C" es el especificador de enlace. Tomemos un ejemplo (el código c++ mencionado aquí):

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

La línea 1 declara pfun para apuntar a una función de C++, porque carece de un especificador de enlace.

Por lo tanto, la línea 2 declara que foo es una función C que toma un puntero a una función C++.

La línea 5 intenta llamar a foo con un puntero a g, una función C, un tipo mis-match.

Diff en el enlace del nombre de la función:

Tomemos dos archivos diferentes:

Uno con extern "c" enlace (file1.cpp):

#include <iostream>
using namespace std;

extern "C"
{
void foo (int a, int b)
{
    cout << "here";
}
}

int main ()
{
    foo (10,20);
    return 0;
}

Uno sin extern "c" enlace (file2.cpp):

#include <iostream>
using namespace std;

void foo (int a, int b)
{
    cout << "here";
}

int main ()
{
    foo (10,20);
    return 0;
}

Ahora compile estos dos y revisen la bomba.

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

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

Con el enlace externo "C", no hay un cambio de nombre para la función foo. Por lo tanto, cualquier programa que lo esté usando (suponiendo que hagamos una lib compartida con él) puede llamar directamente a foo (con funciones auxiliares como dlsym y dlopen) sin considerar ningún efecto de alteración de nombres.

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 

Por otro lado, cuando no extern "C" se está utilizando, func: foo está destrozado con algunas reglas predefinidas (conocidas por el compilador / enlazador que se está utilizando) y por lo que una aplicación puede no llamarla directamente desde ella especificando el nombre como foo. Sin embargo, puede llamarlo con el nombre mangled (_Z3fooii en este caso) si lo desea, pero nadie lo usa por la razón obvia.

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   

Esta página también es una buena lectura para este tema en particular.

Un artículo bonito y claramente explicado sobre llamar a la convención: 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-26 01:28:50

" el nombre f2 tiene un vínculo con el lenguaje C++ " En el lenguaje C++ linkage no solo el nombre de la función lo define, sino también el tipo de argumentos y el valor devuelto. en este caso usted tiene: void f2 (void); pero se puede definir con él: void f2 (int a); sin conflicto porque el enlace los verá como tipos diferentes, una cosa que no sería capaz de hacer en lenguaje C.

" el tipo de la función tiene enlace de lenguaje C" No conozco los detalles, pero conozco el alto nivel. Basicamente hace que una función compilada en C++ sea enlazable desde C. Si recuerdo correctamente en C y en C++ la forma en que los parámetros se pasan a una función es diferente. En este caso la función f2 pasará los parámetros como lo hace el compilador de C. de esta manera la función será enlazable tanto desde C como desde 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
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

El nombre FUNC se declara con el enlace "C" porque dice extern "C" en la primera línea.

El nombre f2 tiene enlace C++ porque es el predeterminado, y no se da ningún otro enlace en la línea dos.

El hecho de que el nombre f2 se use para referirse a una función con enlace C no cambia el enlace del nombre .

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-19 15:21:40

Tiene que ver con el ABI (Application Binary Interface) del programa.

Como una API especifica la interfaz externa del código fuente de un programa, una ABI especifica la interfaz externa del código binario del programa (la versión compilada).


Originalmente, las funciones C simplemente tenían algunas formas diferentes. Algo así como

int foo(int);

Sería precedido por un subrayado por el compilador, para formar _foo, y luego exportado para ser puesto a disposición de otras aplicaciones.

Sin embargo, eso no fue suficiente. Si nos fijamos en la API de Windows, por ejemplo, verá cosas como:

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

Esto se debe a que no hay manera de distinguir entre las sobrecargas de una función simplemente mirando el nombre de la función, por lo que la gente comenzó a cambiarlas agregando un sufijo Ex (o similar).

Esto llegó a ser bastante feo, y todavía no permitía la sobrecarga del operador, que fue presentado en C++. Debido a esto, se le ocurrió a C++ nombre mangling, poner información adicional en el nombre de la función, como los tipos de datos de sus parámetros, y hacerlo algo críptico con muchos símbolos @.

Todo estaba bien, excepto que no estaba completamente estandarizado.

Por supuesto, a medida que surgieron nuevos lenguajes y compiladores, cada uno creó su propio esquema, algunos incompatibles con otros. Así que si necesitas importar o exportar una función externa, necesita especificar qué tipo de ABI debe buscar el compilador, de ahí el extern "C++" que tiene allí.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-19 19:20:10

¿Qué significa todo esto? Por ejemplo, ¿qué enlace tiene la función f2 (), enlace del lenguaje C o C++?

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

Lo que estás llamando la" función f2 () " tiene dos aspectos en su enlace:

  • el mangling o no de su nombre en la tabla de símbolos (que tiene enlace de lenguaje C++), y
  • la convención que llama a C o C++ es necesaria si la función se llama (C).

Para llamar a f2() encontrará su nombre aka símbolo en el archivo objeto, que será una versión destrozada de"función llamada f2 sin argumentos". Puede verificar esto trivialmente compilando el código anterior e inspeccionando el objeto (por ejemplo, w / GNU tools nm --demangle).

Pero para llamar a la función, las convenciones para las condiciones previas y posteriores vuelven a registrar el uso, la configuración de la pila, etc. son los de funciones de A C. Es legal que las funciones de C y C++ tengan diferentes convenciones de llamada, y podría hacerse, por ejemplo, para facilitar el manejo de excepciones de C++.

Por favor explique las diferencias en el archivo objeto: nombre de una función con enlace de lenguaje C y enlace de lenguaje C++.

  • para el enlace C, " f2 " sería el símbolo en el archivo objeto resultante de f2()
  • para el enlace C++, alguna versión mutilada de "función llamada f2 sin argumentos" (para GNU, _Z2f2v que demangles a f2())

Tipo de función con enlace de lenguaje C y enlace de lenguaje C++.

Como se mencionó anteriormente, se trata de la convención de uso de registro/pila para llamar al código en la dirección de la función. Esta meta-información no se almacena necesariamente en la información de la tabla de símbolos del objeto(y ciertamente no es parte de la clave de nombre del símbolo en sí).

Además, debido a que cada función adopta una de las convenciones de llamada, un compilador necesita saber la convención de llamada a usar cuando sigue un puntero a una función: con esa información, creo que el código restante en la pregunta se convierte en claro.

Hay una excelente discusión en http://developers.sun.com/solaris/articles/mixing.html - en particular recomiendo la sección Trabajando con Punteros a Funciones.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-20 03:06:27

Como todos sabemos en C/C++ la traducción de código se compone de dos fases principales: compilación y enlace. Cuando el compilador genera archivos objeto, pasa información al enlazador especificando en qué archivos objeto se llama o se hace referencia a la función dada. En C es así, la función tiene un nombre y una definición coincidente.

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

Y después de la compilación archivo1.obj almacena el código y la información sobre la definición del símbolo foo.

Pero cuando C++ viene en los nombres de los símbolos se vuelve más complicado. Una función puede estar sobrecargada o ser miembro de una clase. Pero el enlazador no quiere saberlo. Para preservar la simplicidad y la reutilización de los enlazadores más antiguos, necesita un solo nombre si foo es:

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

Pero ya no se puede llamar solo foo, así que aquí viene el nombre mangling. Y podemos obtener del compilador algunas variaciones como foo_void, foo_int, foo_void_classa. Y, finalmente, el enlazador es feliz ya que todos los que se ven como símbolos simples.

Cuando queremos para llamar a la función foo compilada con el compilador de C en código C++ debemos decirle al compilador que queremos que foo sea foo estilo C y no foo_void como podría suponer el compilador de C++. Se hace usando:

extern "C" void foo();

Ahora el compilador sabe que foo se compila usando el compilador C y pasará información al enlazador que este código llama a foo. El enlazador coincidirá con la definición foo en file1.obj. Así que es todo lo que pienso.

Algunas otras directivas como cdecl o stdcall son específicas de Windows y decir cómo se están pasando los parámetros en las llamadas a funciones. Sí, para C y C++ es cdecl. Pero las funciones de la API de Windows utilizan la convención stdcall-Pascal (simplicidad e históricamente Microsoft proporcionó un entorno de desarrollo de Windows en Pascal).

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-19 15:03:09

Cada función, tipo de función y objeto tiene un enlace de lenguaje, que se especifica como una cadena de caracteres simple. Por defecto, el enlace es "C++". El único otro enlace de lenguaje estándar es "C". Todos los demás vínculos lingüísticos y las propiedades asociadas con diferentes los vínculos lingüísticos están definidos por la implementación.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-10 23:13:46