¿Por qué necesitamos extern "C"{#include} en C++? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

¿por Qué necesitamos usar:

extern "C" {
#include <foo.h>
}

Específicamente:

  • ¿Cuándo deberíamos usarlo?

  • ¿Qué está sucediendo a nivel de compilador / enlazador que requiere que lo utilicemos?

  • Cómo en términos ¿resuelve esto los problemas que nos obligan a utilizarlo?

Author: Jonathan Leffler, 2008-09-16

10 answers

C y C++ son superficialmente similares, pero cada uno compila en un conjunto de código muy diferente. Cuando se incluye un archivo de cabecera con un compilador C++, el compilador está esperando código C++. Si, sin embargo, es una cabecera C, entonces el compilador espera que los datos contenidos en el archivo de cabecera sean compilados a un formato determinado-el C++ 'ABI', o 'Application Binary Interface', por lo que el enlazador se ahoga. Esto es preferible a pasar datos de C++ a una función que espera datos de C.

(Para entrar en la realidad nitty-gritty, el ABI de C++generalmente 'mangles' los nombres de sus funciones/métodos, por lo que llamando a printf() sin marcar el prototipo como una función de C, el C++ generará código llamando a _Zprintf, además de basura extra al final.)

Entonces: use extern "C" {...}; cuando incluya un encabezado c-es así de simple. De lo contrario, tendrá un desajuste en el código compilado, y el enlazador se ahogará. Sin embargo, para la mayoría de los encabezados, ni siquiera necesitará extern porque la mayoría de los encabezados C del sistema ya tendrán en cuenta el hecho que podrían ser incluidos por código C++ y ya extern su código.

 109
Author: duane,
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
2015-10-15 00:20:08

Extern "C" determina cómo se deben nombrar los símbolos en el archivo de objeto generado. Si se declara una función sin extern "C", el nombre del símbolo en el archivo de objeto usará el mangling de nombre de C++. He aquí un ejemplo.

Dado la prueba.C así:

void foo() { }

Compilar y listar símbolos en el archivo objeto da:

$ g++ -c test.C
$ nm test.o
0000000000000000 T _Z3foov
                 U __gxx_personality_v0

La función foo en realidad se llama "_Z3foov". Esta cadena contiene información de tipo para el tipo devuelto y los parámetros, entre otras cosas. Si en lugar de escribir la prueba.C así:

extern "C" {
    void foo() { }
}

Luego compila y mira los símbolos:

$ g++ -c test.C
$ nm test.o
                 U __gxx_personality_v0
0000000000000000 T foo

Se obtiene el enlace C. El nombre de la función" foo "en el archivo de objeto es simplemente" foo", y no tiene toda la información de tipo elegante que proviene de name mangling.

Generalmente se incluye un encabezado dentro de extern "C" {} si el código que va con él fue compilado con un compilador de C pero se está tratando de llamarlo desde C++. Al hacer esto, le estás diciendo al compilador que todas las declaraciones en el encabezado se utilizará el enlace C. Cuando vinculas tu código, tu .o los archivos contendrán referencias a "foo", no a" _Z3fooblah", que con suerte coincide con lo que está en la biblioteca contra la que está enlazando.

La mayoría de las bibliotecas modernas colocarán guardias alrededor de tales encabezados para que los símbolos se declaren con el enlace correcto. por ejemplo, en muchos de los encabezados estándar encontrará:

#ifdef __cplusplus
extern "C" {
#endif

... declarations ...

#ifdef __cplusplus
}
#endif

Esto asegura que cuando el código C++ incluye el encabezado, los símbolos en su archivo de objeto coincidan con lo que hay en la biblioteca C. Solo deberías tener que poner extern" C " {} alrededor de tu cabecera C si es vieja y no tiene estos guardias ya.

 104
Author: tgamblin,
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
2008-09-16 00:07:06

En C++, puede tener diferentes entidades que comparten un nombre. Por ejemplo, aquí hay una lista de funciones todas llamadas foo :

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

Para diferenciar entre todos ellos, el compilador de C++ creará nombres únicos para cada uno en un proceso llamado name-mangling o decorating. Los compiladores de C no hacen esto. Además, cada compilador de C++ puede hacer esto de una manera diferente.

Extern " C " le dice al C++ el compilador no realizará ningún cambio de nombre en el código dentro de las llaves. Esto le permite llamar a funciones de C desde C++.

 20
Author: Trent,
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
2017-10-05 07:35:46

Tiene que ver con la forma en que los diferentes compiladores realizan la modificación de nombres. Un compilador de C++ alterará el nombre de un símbolo exportado desde el archivo de cabecera de una manera completamente diferente a la que haría un compilador de C, por lo que cuando intente vincular, obtendrá un error de enlazador diciendo que faltan símbolos.

Para resolver esto, le decimos al compilador de C++ que se ejecute en modo "C", por lo que realiza la modificación de nombres de la misma manera que lo haría el compilador de C. Una vez hecho esto, se corrigen los errores del enlazador.

 14
Author: 1800 INFORMATION,
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
2008-09-15 23:24:32

¿Cuándo debemos usarlo?

Cuando está vinculando libarios de C a archivos objeto de C++

Lo que está sucediendo en el nivel de compilador / enlazador que nos requiere para usarlo?

C y C++ usan diferentes esquemas para nombrar símbolos. Esto le dice al enlazador que use el esquema de C al enlazar en la biblioteca dada.

Cómo en términos de compilación / enlace ¿resuelve esto los problemas que ¿requerimos que lo usemos?

Usando la C el esquema de nombres le permite hacer referencia a símbolos de estilo C. De lo contrario, el enlazador probaría símbolos de estilo C++que no funcionarían.

 11
Author: Tony M,
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
2008-09-15 23:29:41

C y C++ tienen reglas diferentes sobre los nombres de los símbolos. Los símbolos son cómo el enlazador sabe que la llamada a la función " openBankAccount "en un archivo objeto producido por el compilador es una referencia a esa función que usted llamó" openBankAccount " en otro archivo objeto producido a partir de un archivo fuente diferente por el mismo compilador (o compatible). Esto le permite hacer un programa a partir de más de un archivo de origen, lo cual es un alivio cuando se trabaja en un proyecto grande.

En C la regla es muy simple, los símbolos están todos en un solo espacio de nombre de todos modos. Así que el entero "socks "se almacena como" socks "y la función count_socks se almacena como"count_socks".

Los enlazadores se construyeron para C y otros lenguajes como C con esta simple regla de nomenclatura de símbolos. Así que los símbolos en el enlazador son simples cadenas.

Pero en C++ el lenguaje le permite tener espacios de nombres, polimorfismo y varias otras cosas que entran en conflicto con una regla tan simple. Las seis funciones polimórficas llamadas " añadir" necesita tener símbolos diferentes, o el incorrecto será utilizado por otros archivos objeto. Esto se hace "destrozando" (es un término técnico) los nombres de los símbolos.

Al vincular código de C++ a bibliotecas o código de C, necesita "C" externo cualquier cosa escrita en C, como archivos de encabezado para las bibliotecas de C, para decirle a su compilador de C++ que estos nombres de símbolos no deben ser modificados, mientras que el resto de su código de C++ por supuesto debe ser modificado o no funcionará.

 10
Author: tialaramex,
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
2008-09-15 23:28:54

Debe usar extern "C" cada vez que incluya un encabezado que defina funciones que residen en un archivo compilado por un compilador de C, utilizado en un archivo de C++. (Muchas bibliotecas estándar de C pueden incluir esta comprobación en sus encabezados para que sea más simple para el desarrollador)

Por ejemplo, si tiene un proyecto con 3 archivos, util.c, útil.h, y main.cpp y tanto el .c y .los archivos cpp se compilan con el compilador de C++ (g++, cc, etc.), entonces no es realmente necesario, e incluso puede causar errores de enlazador. Si su el proceso de compilación utiliza un compilador C regular para util.c, entonces usted necesitará utilizar extern " C " al incluir util.h.

Lo que está sucediendo es que C++ codifica los parámetros de la función en su nombre. Así es como funciona la sobrecarga de funciones. Todo lo que tiende a suceder a una función C es la adición de un guion bajo ("_") al principio del nombre. Sin usar extern " C " el enlazador buscará una función llamada doSomething@ @ int @ float () cuando el nombre real de la función sea _DoSomething() o simplemente DoSomething().

Usar extern "C" resuelve el problema anterior diciéndole al compilador de C++ que debería buscar una función que siga la convención de nomenclatura de C en lugar de la de C++.

 7
Author: HitScan,
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
2008-09-15 23:45:40

La construcción extern "C" {} indica al compilador que no realice cambios en los nombres declarados dentro de las llaves. Normalmente, el compilador de C++ "mejora" los nombres de las funciones para que codifiquen la información de tipo sobre los argumentos y el valor de retorno; esto se denomina mangled name. La construcción extern "C" evita el destrozo.

Normalmente se usa cuando el código C++ necesita llamar a una biblioteca de lenguaje C. También se puede usar cuando se expone una función de C++ (de una DLL, por ejemplo) a C cliente.

 6
Author: Paul Lalonde,
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
2008-09-15 23:30:25

El compilador de C++ crea nombres de símbolos diferentes al compilador de C. Por lo tanto, si está tratando de hacer una llamada a una función que reside en un archivo C, compilado como código C, debe decirle al compilador de C++ que los nombres de símbolos que está tratando de resolver son diferentes a los predeterminados; de lo contrario, el paso de enlace fallará.

 6
Author: mbyrne215,
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
2008-09-16 02:07:52

Esto se usa para resolver problemas de alteración de nombres. extern C significa que las funciones están en una API de estilo C "plana".

 5
Author: Eric Z Beard,
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
2008-09-15 23:25:09