@Litherum En la parte superior de mi cabeza, le está diciendo al compilador que compile ese alcance de código usando C, dado que tiene un compilador cruzado. Además, significa que tiene un archivo Cpp donde tiene esa foo()función.
ha9u63ar
1
@ ha9u63ar Está 'fuera de mi cabeza'. Todo el resto de tu comentario también es incorrecto. Te recomiendo que lo elimines.
TamaMcGlinn
Respuestas:
1560
Extern "C" hace que un nombre de función en C ++ tenga un enlace 'C' (el compilador no daña el nombre) para que el código C del cliente pueda enlazar (es decir, usar) su función usando un archivo de encabezado compatible 'C' que contenga solo el declaración de su función. La definición de su función está contenida en un formato binario (que fue compilado por su compilador de C ++) que el enlazador 'C' del cliente vinculará utilizando el nombre 'C'.
Dado que C ++ tiene una sobrecarga de nombres de funciones y C no, el compilador de C ++ no puede usar el nombre de la función como una identificación única para vincular, por lo que desbarata el nombre agregando información sobre los argumentos. El compilador de CA no necesita alterar el nombre, ya que no puede sobrecargar los nombres de funciones en C. Cuando declara que una función tiene un enlace externo "C" en C ++, el compilador de C ++ no agrega información de tipo de argumento / parámetro al nombre utilizado para enlace.
Para que lo sepas, puedes especificar explícitamente el enlace "C" a cada declaración / definición individual o usar un bloque para agrupar una secuencia de declaraciones / definiciones para tener un cierto enlace:
Si le interesan los tecnicismos, se enumeran en la sección 7.5 del estándar C ++ 03, aquí hay un breve resumen (con énfasis en la "C" externa):
extern "C" es una especificación de enlace
Todos los compiladores deben proporcionar un enlace "C"
una especificación de vinculación debe ocurrir solo en el alcance del espacio de nombres
todos los tipos de funciones, nombres de funciones y nombres de variables tienen un enlace de idioma. Vea el comentario de Richard: Solo los nombres de funciones y nombres de variables con enlace externo tienen un enlace de idioma.
dos tipos de funciones con enlaces de idiomas distintos son tipos distintos incluso si son idénticos
las especificaciones de vinculación anidan, la interna determina la vinculación final
la "C" externa se ignora para los miembros de la clase
a lo sumo, una función con un nombre particular puede tener un enlace "C" (independientemente del espacio de nombres)
extern "C" obliga a una función a tener un enlace externo (no puede hacerla estática) Ver el comentario de Richard: 'static' dentro de 'extern "C"' es válido; una entidad así declarada tiene un enlace interno y, por lo tanto, no tiene un enlace de idioma
La vinculación de C ++ a objetos definidos en otros lenguajes y a objetos definidos en C ++ de otros lenguajes está definida por la implementación y depende del lenguaje. Solo cuando las estrategias de diseño de objetos de las implementaciones de dos idiomas son lo suficientemente similares, se puede lograr dicha vinculación
El compilador de C no usa la manipulación que hace C ++. Entonces, si desea llamar a la interfaz ac desde un programa de c ++, debe declarar claramente que la interfaz c es "c externa".
Sam Liao
59
@Faisal: no intente vincular el código creado con diferentes compiladores de C ++, incluso si las referencias cruzadas son todas "externas" C "'. A menudo hay diferencias entre los diseños de las clases, o los mecanismos utilizados para manejar las excepciones, o los mecanismos utilizados para garantizar que las variables se inicialicen antes del uso, u otras diferencias similares, además de que puede necesitar dos bibliotecas de soporte de tiempo de ejecución C ++ separadas (una para cada compilador)
Jonathan Leffler
8
'extern "C" obliga a una función a tener un enlace externo (no puede hacerla estática) "es incorrecta. 'static' dentro de 'extern "C"' es válido; una entidad así declarada tiene un enlace interno y, por lo tanto, no tiene un enlace de idioma.
Richard Smith
14
'todos los tipos de funciones, nombres de funciones y nombres de variables tienen un enlace de idioma' también es incorrecto. Solo los nombres de funciones y nombres de variables con enlace externo tienen un enlace de idioma.
Richard Smith
99
Tenga en cuenta que extern "C" { int i; }es una definición. Esto puede no ser lo que pretendía, al lado de la no definición de void g(char);. Para que no sea una definición, necesitaría extern "C" { extern int i; }. Por otro lado, la sintaxis de una declaración sin llaves hace que la declaración no sea una definición: extern "C" int i;es lo mismo queextern "C" { extern int i; }
aschepler el
327
Solo quería agregar un poco de información, ya que no lo he visto publicado aún.
Muy a menudo verá el código en los encabezados C de esta manera:
#ifdef __cplusplusextern"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 se puede también todavía utilizarlo con su código heredado C, donde la macro no definida, por lo que no verá la única construcción de C ++.
Aunque, también he visto código C ++ como:
extern"C"{#include"legacy_C_header.h"}
que me imagino logra casi lo mismo.
No estoy seguro de qué manera es mejor, pero he visto ambos.
Hay una clara diferencia. En el caso de lo primero, si compila este archivo con el compilador gcc normal, generará un objeto donde el nombre de la función no está alterado. Si luego vincula los objetos C y C ++ con el vinculador, NO encontrará las funciones. Deberá incluir esos archivos de "encabezado heredado" con la palabra clave externa como en su segundo bloque de código.
Anne van Rossum el
8
@Anne: el compilador de C ++ buscará también nombres sin desenmascarar, porque vio extern "C"en el encabezado). Funciona muy bien, usé esta técnica muchas veces.
Ben Voigt
20
@ Anne: Eso no está bien, el primero también está bien. El compilador de C lo ignora y tiene el mismo efecto que el segundo en C ++. Al compilador no le importa si encuentra extern "C"antes o después de incluir el encabezado. Para cuando llega al compilador, es solo una larga secuencia de texto preprocesado de todos modos.
Ben Voigt
8
@ Anne, no, creo que te ha afectado algún otro error en la fuente, porque lo que estás describiendo está mal. Ninguna versión de g++esto se equivocó, para ningún objetivo, en ningún momento en los últimos 17 años al menos. El punto principal del primer ejemplo es que no importa si usa un compilador C o C ++, no se realizará ningún cambio de nombre para los nombres en el extern "C"bloque.
Jonathan Wakely
77
"cuál es mejor": sin duda, la primera variante es mejor: permite incluir el encabezado directamente, sin requisitos adicionales, tanto en código C como en C ++. El segundo enfoque es una solución alternativa para los encabezados C, el autor olvidó los protectores C ++ (sin embargo, si estos se agregan después, se aceptan declaraciones externas "C" anidadas ...).
Aconcagua
267
Descompile un g++binario generado para ver qué está pasando
main.cpp
void f(){}void g();extern"C"{void ef(){}void eg();}/* Prevent g and eg from being optimized away. */void h(){ g(); eg();}
8:00000000000000007 FUNC GLOBAL DEFAULT 1 _Z1fv9:00000000000000077 FUNC GLOBAL DEFAULT 1 ef10:000000000000000e17 FUNC GLOBAL DEFAULT 1 _Z1hv11:00000000000000000 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_12:00000000000000000 NOTYPE GLOBAL DEFAULT UND _Z1gv13:00000000000000000 NOTYPE GLOBAL DEFAULT UND eg
Interpretación
Vemos eso:
efy egse almacenaron en símbolos con el mismo nombre que en el código
los otros símbolos fueron destrozados. Vamos a deshacerlos:
Conclusión: los dos tipos de símbolos siguientes no fueron destrozados:
definido
declarado pero indefinido ( Ndx = UND), que se proporcionará en el enlace o en el tiempo de ejecución desde otro archivo de objeto
Entonces necesitará extern "C"ambos cuando llame:
C de C ++: diga g++que espere símbolos sin desencadenar producidos porgcc
C ++ desde C: diga g++que genere símbolos sin desenvolver para gccusar
Cosas que no funcionan en el exterior C
Resulta obvio que cualquier característica de C ++ que requiera el cambio de nombre no funcionará en el interior extern C:
extern"C"{// Overloading.// error: declaration of C function ‘void f(int)’ conflicts withvoid f();void f(int i);// Templates.// error: template with C linkagetemplate<class C>void f(C i){}}
#ifndef C_H#define C_H/* This ifdef allows the header to be used from both C and C++
* because C does not know what this extern "C" thing is. */#ifdef __cplusplusextern"C"{#endifint f();#ifdef __cplusplus}#endif#endif
#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"{#endifint 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);}
La mejor respuesta ya que usted 1) menciona explícitamente que lo extern "C" {ayuda a llamar a funciones C no desglosadas desde programas C ++ , así como a funciones C ++ no desorganizadas desde programas C , que otras respuestas no hacen tan obvias, y 2) porque muestra ejemplos distintos de cada. ¡Gracias!
Gabriel Staples
3
Me gusta mucho esta respuesta
autoarranque el
44
Sin duda, la mejor respuesta ya que muestra cómo llamar a funciones sobrecargadas desde c
Gaspa79
1
@JaveneCPPMcGowan, ¿qué te hace pensar que tenía un profesor de C ++? :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
205
En cada programa 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 forma única una función en el programa.
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 clases, funciones miembro, especificaciones de excepción, no es posible usar simplemente el nombre de la función como el nombre del símbolo. Para resolver eso, C ++ usa el llamado cambio de nombre, que transforma el nombre de la función y toda la información necesaria (como el número y el tamaño de los argumentos) en una cadena de aspecto extraño procesada solo por el compilador y el enlazador.
Entonces, si especifica que una función sea externa C, el compilador no realiza el cambio de nombre con ella y se puede acceder directamente usando su nombre de símbolo como el nombre de la función.
Esto resulta útil durante el uso dlsym()y dlopen()para llamar a tales funciones.
¿Qué quieres decir con práctico? ¿El nombre del símbolo = nombre de la función haría que el nombre del símbolo pasado a dlsym sea conocido, u otra cosa?
Error
1
@Error: sí. Es esencialmente imposible en el caso general dlopen () una biblioteca compartida de C ++ dada solo un archivo de encabezado y elegir la función correcta para cargar. (En x86, hay una especificación de cambio de nombre publicada en forma de Itanium ABI que todos los compiladores x86 que conozco usan para destrozar nombres de funciones de C ++, pero nada en el lenguaje requiere esto).
Jonathan Tomer
52
C ++ manipula nombres de funciones para crear un lenguaje orientado a objetos a partir de un lenguaje de procedimiento
La mayoría de los lenguajes de programación no están construidos sobre los lenguajes de programación existentes. C ++ está construido sobre C, y además es un lenguaje de programación orientado a objetos construido a partir de un lenguaje de programación procesal, y por esa razón hay expresiones de C ++ extern "C"que proporcionan compatibilidad con C.
Veamos el siguiente ejemplo:
#include<stdio.h>// Two functions are defined with the same name// but have different parametersvoid printMe(int a){
printf("int: %i\n", a);}void printMe(char a){
printf("char: %c\n", a);}int main(){
printMe("a");
printMe(1);return0;}
El compilador de CA no compilará el ejemplo anterior, porque la misma función printMese define dos veces (a pesar de que tienen diferentes parámetros int avs char a).
gcc -o printMe printMe.c && ./printMe; 1 error PrintMe se define más de una vez.
Un compilador de C ++ compilará el ejemplo anterior. No le importa que printMese defina dos veces.
g ++ -o printMe printMe.c && ./printMe;
Esto se debe a que un compilador de C ++ renombra implícitamente ( alterar ) las funciones en función de sus parámetros. En C, esta característica no era compatible. Sin embargo, cuando C ++ fue construido sobre C, el idioma se diseñó para ser orientado a objetos, y necesitaba para soportar la capacidad de crear diferentes clases con los métodos (funciones) del mismo nombre, y para anular métodos ( método imperiosas ) basados en diferentes parámetros
extern "C" dice "no alterar los nombres de las funciones C"
Sin embargo, imagine que tenemos un archivo C heredado llamado "parent.c" que contiene includenombres de funciones de otros archivos C heredados, "parent.h", "child.h", etc. Si se ejecuta el archivo "parent.c" heredado a través de un compilador de C ++, los nombres de las funciones se modificará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 también deberían ser destrozado El cambio de nombres de funciones en un programa C complejo, aquellos con muchas dependencias, puede conducir a un código roto; por lo tanto, puede ser conveniente proporcionar una palabra clave que pueda indicarle al compilador de C ++ que no altere el nombre de una función.
La extern "C"palabra clave le dice a un compilador de C ++ que no altere (renombre) los nombres de las funciones de C.
¿No podemos usar extern "C"si solo tenemos un dllarchivo? Quiero decir, si no tenemos un archivo de encabezado y solo tenemos un archivo fuente (solo implementaciones) y el uso de su función a través del puntero de función. en este estado, solo utilizamos funciones (independientemente de su nombre).
BattleTested
@tfmontague, ¡para mí lo has acertado! derecho en la cabeza
Artanis Zeratul
29
Ningún encabezado C se puede hacer compatible con C ++ simplemente envolviendo en "C" externa. Cuando los identificadores en un encabezado C entran en conflicto con las palabras clave de C ++, el compilador de C ++ se quejará de esto.
Por ejemplo, he visto fallar el siguiente código en un g ++:
extern"C"{struct method {intvirtual;};}
Tiene sentido, pero es algo a tener en cuenta al portar código C a C ++.
extern "C"significa usar el enlace C, como se describe en otras respuestas. No significa "compilar los contenidos como C" ni nada. int virtual;es inválido en C ++ y especificar enlaces diferentes no cambia eso.
MM
1
... o modo en general, cualquier código que contenga un error de sintaxis no se compilará.
Valentin Heinitz el
44
@ValentinHeinitz, naturalmente, aunque usar "virtual" como identificador en C no es un error de sintaxis. Solo quería señalar que no puede usar automáticamente ningún encabezado C en C ++ colocando la "C" externa a su alrededor.
Sander Mertens
28
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 está destrozado .
Maltratado es el término que se usa generalmente ... No creo que haya visto 'decorado' con este significado.
Matthew Scharley
1
Microsoft (al menos parcialmente) usa decorados en lugar de maltratados en su documentación. incluso nombran su herramienta para decorar (también conocido como deshacer) un nombre undname.
René Nyffenegger
20
Informa al compilador de C ++ que busque los nombres de esas funciones en un estilo C al vincular, porque los nombres de las funciones compiladas en C y C ++ son diferentes durante la etapa de vinculación.
Extern "C" está destinado a ser reconocido por un compilador de C ++ y notificar al compilador que la función anotada se compila (o se compilará) 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 que los archivos dll (biblioteca de enlaces dinámicos) hicieran que la función main () fuera "exportable", de modo que pueda usarse más tarde en otro ejecutable desde dll. Quizás un ejemplo de dónde solía usarlo puede ser útil.
DLL
#include<string.h>#include<windows.h>usingnamespace 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<string.h>#include<windows.h>usingnamespace std;typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dllFunction mainDLLFunc;//make a variable for function placeholderint main(){char winDir[MAX_PATH];//will hold path of above dllGetCurrentDirectory(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 dllif(DLL==NULL){FreeLibrary((HMODULE)DLL);//if load fails exitreturn0;}
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 nameif(mainDLLFunc==NULL){FreeLibrary((HMODULE)DLL);//if it fails exitreturn0;}
mainDLLFunc();//run exported function FreeLibrary((HMODULE)DLL);}
Falso. extern "C"y __declspec(dllexport)no están relacionados El primero controla la decoración del símbolo, el último es responsable de crear una entrada de exportación. También puede exportar un símbolo usando la decoración de nombre C ++. Además de perder completamente el punto de esta pregunta, también hay otros errores en el ejemplo de código. Por un lado, mainexportado desde su DLL no declara un valor de retorno. O llamar a la convención, para el caso. Al importar, atribuye una convención de llamada aleatoria ( WINAPI) y utiliza el símbolo incorrecto para compilaciones de 32 bits (debería ser _maino _main@0). Lo siento, -1.
Inspectable
1
Eso solo se repite, que no sabes lo que estás haciendo, pero hacerlo de esta manera parece funcionar para ti, para alguna lista no revelada de plataformas de destino. No abordó los problemas que planteé en mi comentario anterior. Esto todavía es un voto negativo, debido a que está completamente equivocado (hay más, que no cabía en un solo comentario).
Inspectable
1
Publicar una respuesta en Stack Overflow implica que sabes lo que estás haciendo. Esto es de esperarse. En cuanto a su intento de "evitar la corrupción de la pila en ejecución" : la firma de su función especifica un valor de retorno de tipo void*, pero su implementación no devuelve nada. Eso volará muy bien ...
Inspectable
1
Si implementa algo, que parece funcionar, por pura suerte, entonces claramente no sabe lo que está haciendo (su muestra de "trabajo" cae en esa categoría). Es un comportamiento indefinido, y parecer trabajar es una forma válida de comportamiento indefinido. Aún no está definido. Le agradecería mucho si ejerciera más diligencia en el futuro. Parte de eso podría ser eliminar esta respuesta propuesta.
Inspeccionable
1
Está reinterpretando una función que no devuelve nada como una función que devuelve un puntero. Es pura suerte que x86 sea muy indulgente con respecto a las firmas de función que no coinciden, y en particular los valores de retorno de tipo integral. Su código funciona solo por coincidencia. Si no está de acuerdo, debe explicar por qué su código funciona de manera confiable.
Inspectable
5
extern "C"es una especificación de enlace que se utiliza para llamar a funciones C en los archivos fuente de Cpp . Podemos llamar a funciones C, escribir variables e incluir encabezados . La función se declara en entidad externa y se define afuera. La sintaxis es
Tipo 1:
extern"language"function-prototype
Tipo 2:
extern"language"{function-prototype
};
p.ej:
#include<iostream>usingnamespace std;extern"C"{#include<stdio.h>// Include C Headerint n;// Declare a Variablevoid func(int,int);// Declare a function (function prototype)}int main(){
func(int a,int b);// Calling function . . .return0;}// Function definition . . .void func(int m,int n){////}
Esta respuesta es para los impacientes / tienen plazos para cumplir, solo una explicación parcial / simple está a continuación:
en C ++, puede tener el mismo nombre en clase a través de la sobrecarga (por ejemplo, dado que todos tienen el mismo nombre no se pueden exportar tal cual desde dll, etc.) la solución a estos problemas es que se convierten en cadenas diferentes (llamadas símbolos ), los símbolos representan el nombre de la función, también los argumentos, por lo que cada una de estas funciones, incluso con el mismo nombre, se puede identificar de forma única (también llamada, cambio de nombre)
en C, no tiene sobrecarga, el nombre de la función es único (por lo tanto, no se requiere una cadena separada para identificar el nombre de una función de manera única, por lo que el símbolo es el nombre de la función en sí)
Entonces,
en C ++, con el cambio de nombre identifica de forma única cada función
en C, incluso sin el cambio de nombre identifica de forma única cada función
Para cambiar el comportamiento de C ++, es decir, para especificar que el cambio de nombre no debe ocurrir para una función en particular, puede usar "C" externa antes del nombre de la función, por cualquier razón, como exportar una función con un nombre específico desde un dll , para uso de sus clientes.
Lea otras respuestas, para obtener respuestas más detalladas / más correctas.
Al mezclar C y C ++ (es decir, a. Llamar 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 de la persona que llama ya se han compilado en binario (muy probablemente, un archivo de biblioteca * .a) usando el compilador correspondiente.
Por lo tanto, necesitamos usar "C" externa para deshabilitar el cambio de nombre en C ++.
Sin entrar en conflicto con otras buenas respuestas, agregaré un poco de mi ejemplo.
Qué hace exactamente el compilador de C ++ : altera los nombres en el proceso de compilación, por lo tanto, debemos decirle al compilador que trate laC implementación especialmente.
Cuando estamos haciendo clases de C ++ y agregando extern "C", le estamos diciendo a nuestro compilador de C ++ que estamos usando la convención de llamadas de C ++.
Motivo (estamos llamando a la implementación de C desde C ++): o bien queremos llamar a la función de C desde C ++ o llamar a la función de C ++ desde C (las clases de C ++ ... etc. no funcionan en C).
Bienvenido a Stack Overflow. Si decide responder una pregunta anterior que tiene respuestas bien establecidas y correctas, agregar una nueva respuesta al final del día puede no darle ningún crédito. Si tiene alguna información nueva distintiva, o si está convencido de que las otras respuestas son incorrectas, agregue una nueva respuesta, pero 'otra respuesta' que proporciona la misma información básica mucho tiempo después de que se formuló la pregunta generalmente gana ' No te ganaré mucho crédito. Francamente, no creo que haya nada nuevo en esta respuesta.
Jonathan Leffler
Bueno, debería haber recordado tu punto - está bien
Susobhan Das
-1
Una función void f () compilada por un compilador de C y una función con el mismo nombre void f () compilada por un compilador de C ++ no son la misma función. Si escribió esa función en C, y luego intentó llamarla desde C ++, entonces el enlazador buscaría la función C ++ y no la encontraría.
extern "C" le dice al compilador de C ++ que tiene una función que fue compilada por el compilador de C. Una vez que le diga que fue compilado por el compilador de C, el compilador de C ++ sabrá cómo llamarlo correctamente.
También permite que el compilador de C ++ compile una función de C ++ de tal manera que el compilador de C ++ pueda llamarla. Esa función sería oficialmente una función de C, pero como está compilada por el compilador de C ++, puede usar todas las funciones de C ++ y tiene todas las palabras clave de C ++.
El compilador de C ++ puede compilar una extern "C"función, y (sujeto a algunas restricciones) será invocable mediante un código compilado por un compilador de C.
foo()
función.Respuestas:
Extern "C" hace que un nombre de función en C ++ tenga un enlace 'C' (el compilador no daña el nombre) para que el código C del cliente pueda enlazar (es decir, usar) su función usando un archivo de encabezado compatible 'C' que contenga solo el declaración de su función. La definición de su función está contenida en un formato binario (que fue compilado por su compilador de C ++) que el enlazador 'C' del cliente vinculará utilizando el nombre 'C'.
Dado que C ++ tiene una sobrecarga de nombres de funciones y C no, el compilador de C ++ no puede usar el nombre de la función como una identificación única para vincular, por lo que desbarata el nombre agregando información sobre los argumentos. El compilador de CA no necesita alterar el nombre, ya que no puede sobrecargar los nombres de funciones en C. Cuando declara que una función tiene un enlace externo "C" en C ++, el compilador de C ++ no agrega información de tipo de argumento / parámetro al nombre utilizado para enlace.
Para que lo sepas, puedes especificar explícitamente el enlace "C" a cada declaración / definición individual o usar un bloque para agrupar una secuencia de declaraciones / definiciones para tener un cierto enlace:
Si le interesan los tecnicismos, se enumeran en la sección 7.5 del estándar C ++ 03, aquí hay un breve resumen (con énfasis en la "C" externa):
todos los tipos de funciones, nombres de funciones y nombres de variables tienen un enlace de idioma.Vea el comentario de Richard: Solo los nombres de funciones y nombres de variables con enlace externo tienen un enlace de idioma.extern "C" obliga a una función a tener un enlace externo (no puede hacerla estática)Ver el comentario de Richard: 'static' dentro de 'extern "C"' es válido; una entidad así declarada tiene un enlace interno y, por lo tanto, no tiene un enlace de idiomafuente
extern "C" { int i; }
es una definición. Esto puede no ser lo que pretendía, al lado de la no definición devoid g(char);
. Para que no sea una definición, necesitaríaextern "C" { extern int i; }
. Por otro lado, la sintaxis de una declaración sin llaves hace que la declaración no sea una definición:extern "C" int i;
es lo mismo queextern "C" { extern int i; }
Solo quería agregar un poco de información, ya que no lo he visto publicado aún.
Muy a menudo verá el código en los encabezados C de esta manera:
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 se puede también todavía utilizarlo con su código heredado C, donde la macro no definida, por lo que no verá la única construcción de C ++.
Aunque, también he visto código C ++ como:
que me imagino logra casi lo mismo.
No estoy seguro de qué manera es mejor, pero he visto ambos.
fuente
extern "C"
en el encabezado). Funciona muy bien, usé esta técnica muchas veces.extern "C"
antes o después de incluir el encabezado. Para cuando llega al compilador, es solo una larga secuencia de texto preprocesado de todos modos.g++
esto se equivocó, para ningún objetivo, en ningún momento en los últimos 17 años al menos. El punto principal del primer ejemplo es que no importa si usa un compilador C o C ++, no se realizará ningún cambio de nombre para los nombres en elextern "C"
bloque.Descompile un
g++
binario generado para ver qué está pasandomain.cpp
Compile y desarme la salida ELF generada :
La salida contiene:
Interpretación
Vemos eso:
ef
yeg
se almacenaron en símbolos con el mismo nombre que en el códigolos otros símbolos fueron destrozados. Vamos a deshacerlos:
Conclusión: los dos tipos de símbolos siguientes no fueron destrozados:
Ndx = UND
), que se proporcionará en el enlace o en el tiempo de ejecución desde otro archivo de objetoEntonces necesitará
extern "C"
ambos cuando llame:g++
que espere símbolos sin desencadenar producidos porgcc
g++
que genere símbolos sin desenvolver paragcc
usarCosas que no funcionan en el exterior C
Resulta obvio que cualquier característica de C ++ que requiera el cambio de nombre no funcionará en el interior
extern C
:Ejemplo mínimo de C ejecutable desde C ++
En aras de la exhaustividad y para los novatos, vea también: ¿Cómo usar archivos fuente C en un proyecto C ++?
Llamar a C desde C ++ es bastante fácil: cada función de C solo tiene un posible símbolo no mutilado, por lo que no se requiere trabajo adicional.
main.cpp
ch
cc
Correr:
Sin
extern "C"
el enlace falla con:porque
g++
espera encontrar un destrozadof
, quegcc
no produjo.Ejemplo en GitHub .
Ejemplo de C ++ ejecutable mínimo desde C
Llamar a C ++ desde C es un poco más difícil: tenemos que crear manualmente versiones no mutiladas de cada función que queremos exponer.
Aquí ilustramos cómo exponer sobrecargas de la función C ++ a C.
C Principal
cpp.h
cpp.cpp
Correr:
Sin
extern "C"
falla con:porque
g++
generó símbolos destrozados quegcc
no pueden encontrar.Ejemplo en GitHub .
Probado en Ubuntu 18.04.
fuente
extern "C" {
ayuda a llamar a funciones C no desglosadas desde programas C ++ , así como a funciones C ++ no desorganizadas desde programas C , que otras respuestas no hacen tan obvias, y 2) porque muestra ejemplos distintos de cada. ¡Gracias!En cada programa 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 forma única una función en el programa.
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 clases, funciones miembro, especificaciones de excepción, no es posible usar simplemente el nombre de la función como el nombre del símbolo. Para resolver eso, C ++ usa el llamado cambio de nombre, que transforma el nombre de la función y toda la información necesaria (como el número y el tamaño de los argumentos) en una cadena de aspecto extraño procesada solo por el compilador y el enlazador.
Entonces, si especifica que una función sea externa C, el compilador no realiza el cambio de nombre con ella y se puede acceder directamente usando su nombre de símbolo como el nombre de la función.
Esto resulta útil durante el uso
dlsym()
ydlopen()
para llamar a tales funciones.fuente
C ++ manipula nombres de funciones para crear un lenguaje orientado a objetos a partir de un lenguaje de procedimiento
La mayoría de los lenguajes de programación no están construidos sobre los lenguajes de programación existentes. C ++ está construido sobre C, y además es un lenguaje de programación orientado a objetos construido a partir de un lenguaje de programación procesal, y por esa razón hay expresiones de C ++
extern "C"
que proporcionan compatibilidad con C.Veamos el siguiente ejemplo:
El compilador de CA no compilará el ejemplo anterior, porque la misma función
printMe
se define dos veces (a pesar de que tienen diferentes parámetrosint a
vschar a
).Un compilador de C ++ compilará el ejemplo anterior. No le importa que
printMe
se defina dos veces.Esto se debe a que un compilador de C ++ renombra implícitamente ( alterar ) las funciones en función de sus parámetros. En C, esta característica no era compatible. Sin embargo, cuando C ++ fue construido sobre C, el idioma se diseñó para ser orientado a objetos, y necesitaba para soportar la capacidad de crear diferentes clases con los métodos (funciones) del mismo nombre, y para anular métodos ( método imperiosas ) basados en diferentes parámetros
extern "C"
dice "no alterar los nombres de las funciones C"Sin embargo, imagine que tenemos un archivo C heredado llamado "parent.c" que contiene
include
nombres de funciones de otros archivos C heredados, "parent.h", "child.h", etc. Si se ejecuta el archivo "parent.c" heredado a través de un compilador de C ++, los nombres de las funciones se modificará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 también deberían ser destrozado El cambio de nombres de funciones en un programa C complejo, aquellos con muchas dependencias, puede conducir a un código roto; por lo tanto, puede ser conveniente proporcionar una palabra clave que pueda indicarle al compilador de C ++ que no altere el nombre de una función.La
extern "C"
palabra clave le dice a un compilador de C ++ que no altere (renombre) los nombres de las funciones de C.Por ejemplo:
extern "C" void printMe(int a);
fuente
extern "C"
si solo tenemos undll
archivo? Quiero decir, si no tenemos un archivo de encabezado y solo tenemos un archivo fuente (solo implementaciones) y el uso de su función a través del puntero de función. en este estado, solo utilizamos funciones (independientemente de su nombre).Ningún encabezado C se puede hacer compatible con C ++ simplemente envolviendo en "C" externa. Cuando los identificadores en un encabezado C entran en conflicto con las palabras clave de C ++, el compilador de C ++ se quejará de esto.
Por ejemplo, he visto fallar el siguiente código en un g ++:
Tiene sentido, pero es algo a tener en cuenta al portar código C a C ++.
fuente
extern "C"
significa usar el enlace C, como se describe en otras respuestas. No significa "compilar los contenidos como C" ni nada.int virtual;
es inválido en C ++ y especificar enlaces diferentes no cambia eso.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 está destrozado .
fuente
undname
.Informa al compilador de C ++ que busque los nombres de esas funciones en un estilo C al vincular, porque los nombres de las funciones compiladas en C y C ++ son diferentes durante la etapa de vinculación.
fuente
Extern "C" está destinado a ser reconocido por un compilador de C ++ y notificar al compilador que la función anotada se compila (o se compilará) en estilo C. De modo que mientras se vincula, se vincula a la versión correcta de la función de C.
fuente
Utilicé 'extern "C"' antes para que los archivos dll (biblioteca de enlaces dinámicos) hicieran que la función main () fuera "exportable", de modo que pueda usarse más tarde en otro ejecutable desde dll. Quizás un ejemplo de dónde solía usarlo puede ser útil.
DLL
exe
fuente
extern "C"
y__declspec(dllexport)
no están relacionados El primero controla la decoración del símbolo, el último es responsable de crear una entrada de exportación. También puede exportar un símbolo usando la decoración de nombre C ++. Además de perder completamente el punto de esta pregunta, también hay otros errores en el ejemplo de código. Por un lado,main
exportado desde su DLL no declara un valor de retorno. O llamar a la convención, para el caso. Al importar, atribuye una convención de llamada aleatoria (WINAPI
) y utiliza el símbolo incorrecto para compilaciones de 32 bits (debería ser_main
o_main@0
). Lo siento, -1.void*
, pero su implementación no devuelve nada. Eso volará muy bien ...extern "C"
es una especificación de enlace que se utiliza para llamar a funciones C en los archivos fuente de Cpp . Podemos llamar a funciones C, escribir variables e incluir encabezados . La función se declara en entidad externa y se define afuera. La sintaxis esTipo 1:
Tipo 2:
p.ej:
fuente
Esta respuesta es para los impacientes / tienen plazos para cumplir, solo una explicación parcial / simple está a continuación:
Entonces,
en C ++, con el cambio de nombre identifica de forma única cada función
en C, incluso sin el cambio de nombre identifica de forma única cada función
Para cambiar el comportamiento de C ++, es decir, para especificar que el cambio de nombre no debe ocurrir para una función en particular, puede usar "C" externa antes del nombre de la función, por cualquier razón, como exportar una función con un nombre específico desde un dll , para uso de sus clientes.
Lea otras respuestas, para obtener respuestas más detalladas / más correctas.
fuente
Al mezclar C y C ++ (es decir, a. Llamar 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 de la persona que llama ya se han compilado en binario (muy probablemente, un archivo de biblioteca * .a) usando el compilador correspondiente.
Por lo tanto, necesitamos usar "C" externa para deshabilitar el cambio de nombre en C ++.
fuente
Sin entrar en conflicto con otras buenas respuestas, agregaré un poco de mi ejemplo.
Qué hace exactamente el compilador de C ++ : altera los nombres en el proceso de compilación, por lo tanto, debemos decirle al compilador que trate la
C
implementación especialmente.Cuando estamos haciendo clases de C ++ y agregando
extern "C"
, le estamos diciendo a nuestro compilador de C ++ que estamos usando la convención de llamadas de C ++.Motivo (estamos llamando a la implementación de C desde C ++): o bien queremos llamar a la función de C desde C ++ o llamar a la función de C ++ desde C (las clases de C ++ ... etc. no funcionan en C).
fuente
Una función void f () compilada por un compilador de C y una función con el mismo nombre void f () compilada por un compilador de C ++ no son la misma función. Si escribió esa función en C, y luego intentó llamarla desde C ++, entonces el enlazador buscaría la función C ++ y no la encontraría.
extern "C" le dice al compilador de C ++ que tiene una función que fue compilada por el compilador de C. Una vez que le diga que fue compilado por el compilador de C, el compilador de C ++ sabrá cómo llamarlo correctamente.
También permite que el compilador de C ++ compile una función de C ++ de tal manera que el compilador de C ++ pueda llamarla. Esa función sería oficialmente una función de C, pero como está compilada por el compilador de C ++, puede usar todas las funciones de C ++ y tiene todas las palabras clave de C ++.
fuente
extern "C"
función, y (sujeto a algunas restricciones) será invocable mediante un código compilado por un compilador de C.