Pregunta ¿Cuál es el efecto de extern "C" en C ++?


¿Qué hace exactamente poner extern "C" en el código C ++ hacer?

Por ejemplo:

extern "C" {
   void foo();
}

1214
2018-06-25 02:10


origen


Respuestas:


Extern "C" hace que un nombre de función en C ++ tenga un enlace 'C' (el compilador 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 declaración de tu función. La definición de su función está contenida en un formato binario (compilado por su compilador 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 compilador 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 compilador 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 compilador 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 declaración / 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"):

  • Extern "C" es una especificación de enlace
  • Cada compilador es necesario para 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 lenguaje   Ver el comentario de Richard: Solo los nombres de funciones y nombres de variables con enlaces externos tienen un enlace de lenguaje
  • dos tipos de funciones con enlaces de lenguaje distintos son tipos distintos, incluso si son idénticos
  • nido de especificaciones de vinculación, el interno determina el enlace final
  • Extern "C" se ignora para los miembros de la clase
  • como máximo, una función con un nombre particular puede tener una vinculación "C" (independientemente del espacio de nombres)
  •  Extern "C" fuerza a una función a tener un enlace externo (no puede hacerlo estático)    Ver el comentario de Richard:    'estático' dentro 'extern "C"' es válido; una entidad así declarada tiene un enlace interno, por lo que no tiene un enlace de lenguaje
  • La vinculación desde C ++ a objetos definidos en otros lenguajes y a objetos definidos en C ++ desde otros lenguajes está definida por la implementación y depende del idioma. Solo cuando las estrategias de disposición de objetos de dos implementaciones de lenguaje son lo suficientemente similares, se puede lograr tal vinculación

1216
2018-06-25 02:12



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 puedes además todavía lo use con su código C heredado, donde la macro es NO definido, por lo que no verá la única construcción 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.


242
2017-10-21 01:08



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 manera ú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 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 compilador y el enlazador.

Entonces, si especifica que una función sea externa C, el compilador no realiza el cambio de nombre y puede ser directamente se accede usando su nombre de símbolo como el nombre de la función.

Esto es útil al usar dlsym() y dlopen() para llamar a tales funciones.


168
2018-06-25 05:22



Vamos a descompilar el archivo objeto generado g ++ para ver lo que sucede dentro de esta implementación.

Generar ejemplo

Entrada:

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

Compilar con GCC 4.8 Linux ELF de salida:

g++ -c a.cpp

Descompile la tabla de símbolos:

readelf -s a.o

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 fueron almacenados 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 fueron no destrozado

  • definido
  • declarado pero indefinido (Ndx = UND), que se proporcionará en el enlace o tiempo de ejecución desde otro archivo de objeto

Entonces necesitarás extern "C" ambos al llamar:

  • C de C ++: decir g++ esperar símbolos sin marcar producidos por gcc
  • C ++ de C: decir g++ para generar símbolos no marcados para gcc usar

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 with
    void f();
    void f(int i);

    // Templates.
    // error: template with C linkage
    template <class C> void f(C i) { }
}

120
2018-05-29 10:06



C ++ manipula nombres de funciones para crear un lenguaje orientado a objetos a partir de un lenguaje de procedimientos

La mayoría de los lenguajes de programación no están basados ​​en los lenguajes de programación existentes. C ++ está construido sobre la parte superior de C, y además es un lenguaje de programación orientado a objetos construido a partir de un lenguaje de programación de procedimientos, y por esa razón hay palabras clave en C ++ como extern que proporcionan compatibilidad hacia atrás con C.

Veamos el siguiente ejemplo:

#include <stdio.h>

// 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;
}

Un compilador de C no compilará 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 compilador de C ++ compilará el ejemplo anterior. No le importa eso printMe está definido dos veces

g ++ -o printMe printMe.c && ./printMe;

Esto se debe a que un compilador C ++ cambia el nombre implícitamente (mangles) funciones basadas en sus parámetros. En C, esta función no era compatible. Sin embargo, cuando C ++ se construyó sobre C, el lenguaje se diseñó para orientarse a objetos y se necesitaba para admitir la capacidad de crear diferentes clases con métodos (funciones) del mismo nombre y para anular métodos (método anulando) basado en diferentes parámetros.

Extern dice "no destroces nombres de funciones"

Sin embargo, imagina que tenemos un archivo C heredado llamado "parent.c" que includes nombres de funciones de otros archivos C heredados, "parent.h", "child.h", etc. Si el archivo heredado "parent.c" se ejecuta a través de un compilador C ++, los nombres de las funciones se destrozarán, y lo harán ya no coinciden 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 desglosarse. Y esto podría volverse bastante desordenado. Por lo tanto, podría ser conveniente proporcionar una palabra clave que le indique al compilador de C ++ que no modifique el nombre de una función.

los extern palabra clave le dice a un compilador de C ++ que no modifique (renombre) los nombres de las funciones. Ejemplo de uso: extern void printMe(int a);


23
2018-02-12 01:50



Cambia el enlace de una función de tal manera que la función es invocable desde C. En la práctica eso significa que el nombre de la función no es destrozado.


22
2018-06-25 02:12



No se compilará ningún encabezado C con "C" externo. Cuando los identificadores en un encabezado C entran en conflicto con las palabras clave C ++, el compilador 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 ++.


21
2018-01-09 22:16



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.


16
2018-06-25 02:12



Extern "C" está destinado a ser reconocido por un compilador de C ++ y para notificar al compilador que la función indicada se compila (o se debe compilar) en estilo C. De modo que mientras se vincula, se vincula a la versión correcta de la función de C.


13
2018-04-10 09:46



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 <string.h>
#include <windows.h>

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 <string.h>
#include <windows.h>

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);
}

5
2017-07-01 15:43