¿Qué es exactamente lo que hace poner extern "C"
en el código de C ++?
Por ejemplo:
extern "C" { void foo(); }
Extern “C” hace que un nombre de función en C ++ tenga un enlace ‘C’ (el comstackdor no destruye el nombre) para que el código C del cliente pueda vincular (es decir usar) su función usando un archivo de encabezado compatible ‘C’ que contenga solo el statement de tu función. La definición de su función está contenida en un formato binario (comstackdo por su comstackdor de C ++) al que el enlazador del cliente ‘C’ se vinculará con el nombre ‘C’.
Como C ++ tiene una sobrecarga de nombres de funciones y C no, el comstackdor de C ++ no puede usar el nombre de la función como un ID único para vincular, por lo que modifica el nombre al agregar información sobre los argumentos. El comstackdor de AC no necesita modificar el nombre ya que no puede sobrecargar los nombres de las funciones en C. Cuando declara que una función tiene una vinculación externa “C” en C ++, el comstackdor de C ++ no agrega información de tipo de argumento / parámetro al nombre utilizado para enlace.
Para que lo sepas, puedes especificar el enlace “C” a cada statement / definición individual explícitamente o usar un bloque para agrupar una secuencia de declaraciones / definiciones para tener un cierto enlace:
extern "C" void foo(int); extern "C" { void g(char); int i; }
Si le interesan los aspectos técnicos, se enumeran en la sección 7.5 del estándar C ++ 03, aquí hay un breve resumen (con énfasis en la opción “C”):
Solo quería agregar un poco de información, ya que no la he visto publicada todavía.
Muy a menudo verá el código en los encabezados C así:
#ifdef __cplusplus extern "C" { #endif // all of your legacy C code here #ifdef __cplusplus } #endif
Lo que esto logra es que le permite usar ese archivo de encabezado C con su código C ++, porque se definirá la macro “__cplusplus”. Pero también puede usarlo con su código C heredado, donde la macro NO está definida, por lo que no verá la construcción única de C ++.
Aunque, también he visto código C ++ como:
extern "C" { #include "legacy_C_header.h" }
lo cual imagino logra casi lo mismo.
No estoy seguro de qué camino es mejor, pero he visto ambos.
En cada progtwig C ++, todas las funciones no estáticas se representan en el archivo binario como símbolos. Estos símbolos son cadenas de texto especiales que identifican de manera única una función en el progtwig.
En C, el nombre del símbolo es el mismo que el nombre de la función. Esto es posible porque en C no hay dos funciones no estáticas que puedan tener el mismo nombre.
Debido a que C ++ permite la sobrecarga y tiene muchas características que C no tiene -como las clases, las funciones miembro, las especificaciones de excepción- no es posible simplemente usar el nombre de la función como el nombre del símbolo. Para resolver eso, C ++ utiliza el denominado mangling, que transforma el nombre de la función y toda la información necesaria (como el número y tamaño de los argumentos) en una cadena de aspecto extraño procesada solo por el comstackdor y el enlazador.
Por lo tanto, si especifica que una función es externa C, el comstackdor no realiza el cambio de nombre y se puede acceder directamente usando su nombre de símbolo como nombre de la función.
Esto es útil al usar dlsym()
y dlopen()
para llamar a tales funciones.
Descompile un binario generado por g++
para ver qué está pasando
Entrada:
void f() {} void g(); extern "C" { void ef() {} void eg(); } /* Prevent g and eg from being optimized away. */ void h() { g(); eg(); }
Comstackr con GCC 4.8 Linux ELF de salida:
g++ -c a.cpp
Descompile la tabla de símbolos:
readelf -s ao
La salida contiene:
Num: Value Size Type Bind Vis Ndx Name 8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv 9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef 10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
Interpretación
Vemos eso:
ef
y, eg
se almacenaron en símbolos con el mismo nombre que en el código
los otros símbolos fueron mutilados. Vamos a desmantelarlos:
$ c++filt _Z1fv f() $ c++filt _Z1hv h() $ c++filt _Z1gv g()
Conclusión: los dos tipos de símbolos siguientes no se destrozaron:
Ndx = UND
), que se proporcionará en el enlace o tiempo de ejecución desde otro archivo de objeto Por lo tanto, necesitará una extern "C"
cuando llame:
g++
que espere los símbolos no marcados producidos por gcc
g++
que genere símbolos no marcados para que gcc
use Cosas que no funcionan en el exterior C
Resulta obvio que cualquier característica de C ++ que requiera el cambio de nombre no se ejecutará dentro de extern C
:
extern "C" { // Overloading. // error: declaration of C function 'void f(int)' conflicts with void f(); void f(int i); // Templates. // error: template with C linkage template void f(C i) { } }
Mínimo ejecutable C desde C ++ ejemplo
Por el bien de la compleción y de los nuevos que hay.
Llamar a C desde C ++ es bastante fácil: cada función C solo tiene un símbolo posible no desfigurado, por lo que no se requiere trabajo adicional.
main.cpp:
#include #include "ch" int main() { assert(f() == 1); }
ch:
#ifndef C_H #define C_H /* This ifdef allows the header to be used from both C and C++. */ #ifdef __cplusplus extern "C" { #endif int f(); #ifdef __cplusplus } #endif #endif
cc:
#include "ch" int f() { return 1; }
Correr:
g++ -c -o main.o -std=c++98 main.cpp gcc -c -o co -std=c89 cc g++ -o main.out main.o co ./main.out
Sin extern "C"
el enlace falla con:
main.cpp:6: undefined reference to `f()'
porque g++
espera encontrar un f
mutilado, que gcc
no produjo.
Ejemplo en GitHub .
Ejemplo mínimo de C ++ ejecutable desde C
Llamar a C ++ desde es un poco más difícil: tenemos que crear manualmente versiones no desfiguradas de cada función que queremos exponer.
Aquí ilustramos cómo exponer las sobrecargas de funciones de C ++ a C.
C Principal:
#include #include "cpp.h" int main(void) { assert(f_int(1) == 2); assert(f_float(1.0) == 3); return 0; }
cpp.h:
#ifndef CPP_H #define CPP_H #ifdef __cplusplus // C cannot see these overloaded prototypes, or else it would get confused. int f(int i); int f(float i); extern "C" { #endif int f_int(int i); int f_float(float i); #ifdef __cplusplus } #endif #endif
cpp.cpp:
#include "cpp.h" int f(int i) { return i + 1; } int f(float i) { return i + 2; } int f_int(int i) { return f(i); } int f_float(float i) { return f(i); }
Correr:
gcc -c -o main.o -std=c89 -Wextra main.c g++ -c -o cpp.o -std=c++98 cpp.cpp g++ -o main.out main.o cpp.o ./main.out
Sin extern "C"
falla con:
main.c:6: undefined reference to `f_int' main.c:7: undefined reference to `f_float'
porque g++
generó símbolos destrozados que gcc
no puede encontrar.
Ejemplo en GitHub .
La mayoría de los lenguajes de progtwigción no están basados en los lenguajes de progtwigción existentes. C ++ está construido sobre la parte superior de C, y además es un lenguaje de progtwigción orientado a objetos construido a partir de un lenguaje de progtwigción de procedimientos, y por esa razón hay palabras clave de C ++ como extern
que proporcionan compatibilidad con C.
Veamos el siguiente ejemplo:
#include // Two functions are defined with the same name // but have different parameters void printMe(int a) { printf("int: %i\n", a); } void printMe(char a) { printf("char: %c\n", a); } int main() { printMe("a"); printMe(1); return 0; }
El comstackdor de CA no comstackrá el ejemplo anterior, porque la misma función printMe
se define dos veces (aunque tienen diferentes parámetros int a
vs char a
).
gcc -o printMe printMe.c && ./printMe;
1 error PrintMe se define más de una vez.
Un comstackdor de C ++ comstackrá el ejemplo anterior. No importa que printMe
esté definido dos veces.
g ++ -o printMe printMe.c && ./printMe;
Esto se debe a que un comstackdor de C ++ renombra implícitamente ( altera ) las funciones en función de sus parámetros. En C, esta función no era compatible. Sin embargo, cuando C ++ se construyó sobre C, el lenguaje fue diseñado para ser orientado a objetos, y necesario para admitir la capacidad de crear diferentes clases con métodos (funciones) del mismo nombre, y para anular métodos ( anulación de método ) en función de diferentes parámetros.
Sin embargo, imagina que tenemos un archivo C heredado llamado “parent.c” que include
nombres de funciones de otros archivos heredados de C, “parent.h”, “child.h”, etc. Si el archivo “parent.c” heredado es ejecutar un comstackdor de C ++, los nombres de las funciones se romperán y ya no coincidirán con los nombres de las funciones especificadas en “parent.h”, “child.h”, etc., por lo que los nombres de las funciones en esos archivos externos necesitarían ser destrozado también. Y esto podría volverse bastante desordenado. Por lo tanto, podría ser conveniente proporcionar una palabra clave que le indique al comstackdor de C ++ que no modifique el nombre de una función.
La palabra clave extern
le dice a un comstackdor de C ++ que no modifique (renombre) los nombres de las funciones. Ejemplo de uso: extern void printMe(int a);
Cambia el enlace de una función de tal manera que la función se puede llamar desde C. En la práctica, eso significa que el nombre de la función no se destroza .
No se comstackrá ningún encabezado C con “C” externo. Cuando los identificadores en un encabezado C entran en conflicto con las palabras clave C ++, el comstackdor C ++ se quejará de esto.
Por ejemplo, he visto el siguiente código fallar en un g ++:
extern "C" { struct method { int virtual; }; }
Un poco tiene sentido, pero es algo a tener en cuenta cuando se transfiere el código C a C ++.
Informa al comstackdor de C ++ que busque los nombres de esas funciones en un estilo C al vincular, porque los nombres de las funciones comstackdas en C y C ++ son diferentes durante la etapa de vinculación.
Extern “C” está destinado a ser reconocido por un comstackdor de C ++ y para notificar al comstackdor que la función indicada se comstack (o se debe comstackr) en estilo C. De modo que mientras se vincula, se vincula a la versión correcta de la función de C.
Utilicé ‘Extern “C”‘ antes para los archivos dll (dynamic link library) para hacer que la función main () sea “exportable” para que pueda ser usada más tarde en otro ejecutable de dll. Quizás un ejemplo de dónde solía usarlo puede ser útil.
DLL
#include #include using namespace std; #define DLL extern "C" __declspec(dllexport) //I defined DLL for dllexport function DLL main () { MessageBox(NULL,"Hi from DLL","DLL",MB_OK); }
exe
#include #include using namespace std; typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll Function mainDLLFunc;//make a variable for function placeholder int main() { char winDir[MAX_PATH];//will hold path of above dll GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe strcat(winDir,"\\exmple.dll");//concentrate dll name with path HINSTANCE DLL = LoadLibrary(winDir);//load example dll if(DLL==NULL) { FreeLibrary((HMODULE)DLL);//if load fails exit return 0; } mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main"); //defined variable is used to assign a function from dll //GetProcAddress is used to locate function with pre defined extern name "DLL" //and matcing function name if(mainDLLFunc==NULL) { FreeLibrary((HMODULE)DLL);//if it fails exit return 0; } mainDLLFunc();//run exported function FreeLibrary((HMODULE)DLL); }
extern "C"
es una especificación de enlace que se utiliza para llamar a las funciones C en los archivos fuente Cpp . Podemos llamar a funciones C, escribir variables e incluir encabezados . La función se declara en una entidad externa y se define afuera. La syntax es
Tipo 1:
extern "language" function-prototype
Tipo 2:
extern "language" { function-prototype };
p.ej:
#include using namespace std; extern "C" { #include // Include C Header int n; // Declare a Variable void func(int,int); // Declare a function (function prototype) } int main() { func(int a, int b); // Calling function . . . return 0; } // Function definition . . . void func(int m, int n) { // // }
Cuando se mezclan C y C ++ (es decir, una llamada a la función C desde C ++ y b) llamar a la función C ++ desde C), el cambio de nombre de C ++ causa problemas de vinculación. Técnicamente hablando, este problema ocurre solo cuando las funciones invocadas ya han sido comstackdas en binario (muy probablemente, un *. Un archivo de biblioteca) usando el comstackdor correspondiente.
Por lo tanto, debemos usar extern “C” para deshabilitar el cambio de nombre en C ++.
Esta respuesta es para los impacientes / tienen plazos para cumplir, solo una parte / explicación simple es la siguiente:
Asi que
en C ++, con nombre que destruye identidades únicas cada función
en C, incluso sin nombre manipulando identidades únicas cada función
Para cambiar el comportamiento de C ++, es decir, para especificar que no se debe cambiar el nombre para una función en particular, puede usar extern “C” antes del nombre de la función, por cualquier razón, como exportar una función con un nombre específico de un dll , para uso de sus clientes.
Lea otras respuestas para obtener respuestas más detalladas / más correctas.