c++ - reales - ¿Qué significa que un nombre o tipo tenga un determinado enlace de idioma?
libro de android studio en español pdf (7)
Según (c) ANSI ISO / IEC 14882: 2003, página 127:
Especificaciones de vinculación nido. Cuando las especificaciones de vinculación se anidan, la más interna determina el idioma. Una especificación de vinculación no establece un alcance. Una especificación de enlace se producirá solo en el ámbito del espacio de nombres (3.3). En una especificación de enlace, el enlace de idioma especificado se aplica a los tipos de funciones de todos los declaradores de funciones, nombres de funciones y nombres de variables introducidos por las declaraciones.
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 en lenguaje C o C ++?
Como lo señaló @Johannes Schaub, no hay una explicación real de lo que esto significa en la Norma, por lo que puede interpretarse de manera diferente en diferentes compiladores.
Por favor explique las diferencias en el archivo objeto:
- el nombre de una función con enlace en lenguaje C y enlace en lenguaje C ++.
- el tipo de una función con enlace en lenguaje C y enlace en lenguaje C ++.
¿Qué significa todo esto? Por ejemplo, ¿qué enlace tiene la función f2 (), enlace en 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á llamando la "función f2 ()" tiene dos aspectos a su enlace:
- la modificación o no de su nombre en la tabla de símbolos (que tiene un enlace en lenguaje C ++), y
- la convención de llamada de C o C ++ es necesaria si se llama a la función (C).
Para llamar a f2()
, encontrará su nombre, también conocido como símbolo en el archivo de objeto, que será una versión dañada de "función llamada f2 que no tiene argumentos". Puede verificar esto de forma trivial 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 el uso de registros previos y posteriores, la configuración de la pila, etc. son las de las funciones en C. Es legal que las funciones de C y C ++ tengan diferentes convenciones de llamada, y podrían realizarse, por ejemplo, para facilitar el manejo de excepciones de C ++.
Por favor explique las diferencias en el archivo objeto: el nombre de una función con enlace en lenguaje C y enlace en lenguaje C ++.
- para el enlace C, "f2" sería el símbolo en el archivo de objeto resultante de
f2()
- para el enlace de C ++, algunas versiones dañadas de "la función llamada f2 no toma argumentos" (para GNU,
_Z2f2v
que se desmangle af2()
)
el tipo de una función con enlace en lenguaje C y enlace en lenguaje C ++.
Como se explicó 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 metainformación no se almacena necesariamente en la información de la tabla de símbolos del objeto (y ciertamente no forma parte de la clave del nombre del símbolo en sí).
Además, debido a que cada función adopta una de las convenciones de llamada, un compilador debe conocer la convención de llamada que se debe utilizar al seguir un puntero a una función: con esa idea, creo que el código restante en la pregunta se vuelve claro.
Hay una excelente discusión en here ; en particular, recomiendo la sección Trabajar con punteros a funciones .
Tiene que ver con la 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 de C simplemente tenían algunas formas diferentes. Algo como
int foo(int);
un prefijo será subrayado por el compilador, para formar _foo
, y luego se exportará para que esté disponible para otras aplicaciones.
Sin embargo, eso no fue suficiente. Si miras la API de Windows, por ejemplo, verás 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 las personas comenzaron a cambiarlas agregando un sufijo Ex
(o similar).
Esto se volvió bastante feo y aún no permitía la sobrecarga del operador, que se presentó en C ++. Debido a esto, a C ++ se le ocurrió la manipulación de nombres , para poner información adicional en el nombre de la función, como los tipos de datos de sus parámetros, y convertirlo en algo críptico con muchos símbolos @
.
Todo estaba bien, excepto que no estaba completamente estandarizado .
Por supuesto, a medida que surgían nuevos lenguajes y compiladores, cada uno tenía su propio esquema, algunos incompatibles con otros. Entonces, si necesita importar o exportar una función externa, debe especificar qué tipo de ABI debe buscar el compilador, de ahí el extern "C++"
que tiene allí.
"el nombre f2 tiene enlace en lenguaje C ++" En el enlace en lenguaje C ++, no solo el nombre de la función lo define, sino también el tipo de argumentos y el valor de retorno. en este caso tienes: void f2 (void); pero puedes definir con él: void f2 (int a); sin conflicto porque el enlace los verá como tipos diferentes, una cosa que no podrías hacer en lenguaje C.
"el tipo de función tiene un enlace en lenguaje C" No conozco los detalles, pero conozco su alto nivel. Básicamente, hace que una función compilada en C ++ se pueda vincular desde C. Si recuerdo correctamente En C y en C ++, la forma en que se pasan los parámetros a una función es diferente. En este caso, la función f2 pasará los parámetros a medida que el compilador C haga esto. De esta manera, la función se podrá vincular tanto desde C como desde C ++.
Cada función, tipo de función y objeto tiene un enlace de idioma, que se especifica como una simple cadena de caracteres. Por defecto, el enlace es "C ++". El único otro enlace de idioma estándar es "C". Todos los enlaces de otros idiomas y las propiedades asociadas con enlaces de diferentes idiomas están definidos por la implementación.
Como todos sabemos, la traducción de códigos C / C ++ se compone de dos fases principales: compilación y enlace. Cuando el compilador genera archivos de objetos, pasa información al vinculador, especificando en qué archivos de objetos se llama o se hace referencia a la función. En C es así, la función tiene un nombre y una definición coincidentes.
// file1.c
void foo(void) {}
Y después de la compilación file1.obj almacena el código y la información sobre la definición del símbolo foo.
Pero cuando C ++ entra en juego, los nombres se vuelven más complicados. 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, se necesita un solo nombre si foo es:
void foo(void) {}
void foo(int) {}
void ClassA::foo(void) {}
Pero ya no puede llamarse simplemente foo, así que aquí viene el nombre de la mutilación. Y podemos obtener del compilador algunas variaciones como foo_void, foo_int, foo_void_classa. Y, finalmente, el enlazador está contento ya que todos los que parecen símbolos simples.
Cuando queremos llamar a la función foo compilada con el compilador C en código C ++, debemos decirle al compilador que queremos que foo sea estilo C y no foo_void como podría suponer el compilador C ++. Se realiza mediante:
extern "C" void foo();
Ahora el compilador sabe que foo se compila con el compilador C y pasará la información al vinculador que este código llama foo. El enlazador lo comparará con la definición de foo en file1.obj. Así que es todo lo que pienso.
Algunas otras directivas, como cdecl o stdcall, son específicas de Windows y explican cómo se pasan los parámetros en las llamadas de función. Sí, para C y C ++ es cdecl. Pero las funciones de la API de Windows usan la convención stdcall - Pascal (la simplicidad e históricamente Microsoft alguna vez proporcionó un entorno de desarrollo de Windows en Pascal).
Enlace de idioma es el término utilizado para el enlace entre fragmentos de código non-C++
C++
y non-C++
. Normalmente, en un programa de C ++, todos los nombres de funciones, tipos de funciones e incluso nombres de variables tienen el enlace de lenguaje C ++ predeterminado.
Un código de objeto de C ++ se puede vincular a otro código de objeto que se produce utilizando algún otro idioma de origen (como C
) utilizando un especificador de vinculación predefinido.
Como debe ser consciente del concepto de name mangling
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 vinculador diferenciar entre nombres comunes (como en el caso de la sobrecarga de funciones). La manipulación de nombres no es conveniente al vincular módulos C con bibliotecas o archivos de objetos compilados con un compilador de C ++. Para evitar la mutilación de nombres en tales casos, se utilizan especificadores de vinculación. En este caso, extern "C"
es el especificador de vinculación. Tomemos un ejemplo (código c ++ mencionado here ):
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 que pfun
apunta a una función de C ++, porque carece de un especificador de vinculación.
La línea 2, por lo tanto, declara que foo es una función C que lleva 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 de desajuste.
Diff en enlace de nombre de función:
Tomemos dos archivos diferentes:
Uno con enlace extern "c"
(archivo1.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 enlace extern "c"
(archivo2.cpp):
#include <iostream>
using namespace std;
void foo (int a, int b)
{
cout << "here";
}
int main ()
{
foo (10,20);
return 0;
}
Ahora compila estos dos y comprueba el objdump.
# g++ file1.cpp -o file1
# objdump -Dx file1
# g++ file2.cpp -o file2
# objdump -Dx file2
Con el enlace externo "C", no hay ningún nombre para la función foo
. Por lo tanto, cualquier programa que lo esté utilizando (asumiendo que creamos una biblioteca compartida a partir de él) puede llamar directamente a foo (con funciones de ayuda como dlsym
y dlopen
) sin considerar los efectos de manipulació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 se está utilizando una extern "C"
, func: foo
se maneja con algunas reglas predefinidas (conocidas por el compilador / enlazador) y la aplicación no puede llamarlo directamente especificando el nombre como foo
. Sin embargo, puedes llamarlo con el nombre destrozado ( _Z3fooii
en este caso) si quieres, 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 agradable y claramente explicado sobre la convención de llamadas: http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
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 enlace "C" porque dice extern "C"
en la primera línea.
El nombre f2
tiene enlace C ++ porque ese es el valor 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 .