2008-09-15 14 views
117

¿Por qué tenemos que utilizar:¿Por qué necesitamos extern "C" {#include <foo.h>} en C++?

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

Específicamente:

  • Cuando debemos usar?

  • ¿Qué está sucediendo al nivel de compilador/enlazador que nos obliga a usarlo?

  • ¿Cómo en términos de compilación/vinculación resuelve los problemas que requieren que lo usemos?

+0

Estoy confundido con lo que quieres decir en el título de tu pregunta ... ¿puedes dar más detalles? –

+29

No estoy seguro de qué otra manera ponerlo. ¿Has leído más allá del título? – Landon

Respuesta

106

C y C++ son superficialmente similares, pero cada uno se compila en un conjunto de códigos muy diferente. Cuando incluye un archivo de encabezado con un compilador C++, el compilador espera el código C++. Sin embargo, si se trata de un encabezado C, el compilador espera que los datos contenidos en el archivo de encabezado se compilen en un determinado formato, 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 esencial y básico, 'ABI s general 'C++ mangles' los nombres de sus funciones/métodos, por lo que llaman printf() sin marcar el prototipo como una función de C, C++ realmente generar código llamando _Zprintf, además basura extra al final.)

Entonces: use extern "C" {...}; cuando se incluye el encabezado de CA, es así de simple. De lo contrario, tendrá una falta de coincidencia en el código compilado y el enlazador se ahogará. Para la mayoría de los encabezados, sin embargo, ni siquiera necesitará el extern porque la mayoría de los encabezados del sistema C ya tendrán en cuenta el hecho de que podrían estar incluidos en el código C++ y ya tienen el código extern.

+1

¿Podrían dar más detalles sobre ** "la mayoría de los encabezados del sistema C ya explicarán el hecho de que podrían estar incluidos en el código C++ y ya externar su código" **? –

+3

@BulatM. Contienen algo como esto: '#ifdef __cplusplus extern" C "{ # endif' Por lo tanto, cuando se incluyen desde un archivo C++ todavía se tratan como un encabezado C. – Calmarius

+1

No ';' necesario al final. No puedo cambiarlo, ya que requiere al menos 6 caracteres para cambiar para aplicar un cambio -, - estúpido – danger89

13

Tiene que ver con la forma en que los diferentes compiladores realizan la manipulación de nombres. Un compilador de C++ destruirá el nombre de un símbolo exportado desde el archivo de encabezado de una manera completamente diferente a la de un compilador de C, de modo que cuando intente vincularse, obtendrá un error de enlazador que indica que faltan símbolos.

Para resolver esto, le decimos al compilador de C++ que se ejecute en el modo "C", por lo que realiza el cambio de nombre de la misma forma que el compilador de C. Una vez hecho esto, los errores del enlazador son corregidos.

5

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

6

El compilador de C++ crea nombres de símbolos de forma diferente que el compilador de C. Por lo tanto, si intenta 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 los símbolos que está tratando de resolver tienen un aspecto diferente al predeterminado; de lo contrario, el paso del enlace fallará.

18

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

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

Con el fin de diferenciar entre todos ellos, el compilador de C++ creará nombres únicos para cada uno en un proceso llamado mangle o decoración. Los compiladores C no hacen esto. Además, cada compilador de C++ puede hacer esto de una manera diferente.

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

10

C y C++ tienen diferentes reglas 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 llamada "openBankAccount" en otro archivo objeto producido desde un archivo fuente diferente por el mismo (o compatible) compilador. Esto le permite crear un programa a partir de más de un archivo fuente, lo que 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 nombres de todos modos. Entonces, el entero "calcetines" se almacena como "calcetines" y la función cuenta_calcetines se almacena como "cuellos_contables".

Los enlazadores se han creado para C y otros lenguajes como C con esta sencilla regla de denominación de símbolos. Entonces los símbolos en el enlazador son solo cadenas simples.

Pero en C++ el lenguaje le permite tener espacios de nombres, y polimorfismo y varias otras cosas que entran en conflicto con una regla tan simple. Las seis funciones polimórficas llamadas "agregar" deben tener diferentes símbolos, o la incorrecta será utilizada por otros archivos de objetos. Esto se hace mediante el "mangle" (es un término técnico) los nombres de los símbolos.

Al vincular código C++ a bibliotecas C o código, necesita todo lo externo "C" escrito en C, como archivos de encabezado para las bibliotecas C, decirle a su compilador C++ que estos nombres de símbolos no deben ser destrozados, mientras que el resto de tu código C++, por supuesto, debe ser mutilado o no funcionará.

10

¿Cuándo deberíamos usarlo?

Cuando está vinculando libaries C en objeto de C++ archivos

Lo que está sucediendo a nivel compilador/enlazador que nos obliga usarlo?

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

¿Cómo en términos de compilación/vinculación se relaciona este resolver los problemas que nos obligan a usarlo?

El uso del esquema de denominación en C le permite hacer referencia a los símbolos del estilo C.De lo contrario, el enlazador probaría símbolos de estilo C++ que no funcionarían.

5

La construcción extern "C" {} indica al compilador que no debe realizar 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 llama nombre mutilado. El constructo extern "C" impide el cambio.

Se utiliza normalmente cuando el código C++ necesita llamar a una biblioteca de lenguaje C. También se puede usar al exponer una función de C++ (desde una DLL, por ejemplo) a clientes C.

104

extern "C" determina cómo deben nombrarse 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 del objeto usará el cambio de nombre de C++. Aquí hay un ejemplo.

TEST.C Dada esta manera:

void foo() { } 

Compilar y una lista de símbolos en el archivo de objeto da:

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

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

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

a continuación, compilar y mirar a los símbolos:

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

Usted recibe C vinculación. El nombre de la función "foo" en el archivo de objeto es solo "foo", y no tiene toda la información de tipo elegante que proviene del cambio de nombre.

Generalmente, se incluye un encabezado dentro de la opción "C" {} si el código que lo acompaña se compiló con un compilador de C pero está intentando llamarlo desde C++. Cuando haces esto, le estás diciendo al compilador que todas las declaraciones en el encabezado usarán el enlace C. Cuando vincula su código, sus archivos .o contendrán referencias a "foo", no a "_Z3fooblah", lo que con suerte coincidirá con lo que esté en la biblioteca con la que está enlazando.

La mayoría de las bibliotecas modernas colocarán protecciones alrededor de tales encabezados para que los símbolos se declaren con el enlace correcto. p.ej. en muchas de las cabeceras estándar encontrará:

#ifdef __cplusplus 
extern "C" { 
#endif 

... declarations ... 

#ifdef __cplusplus 
} 
#endif 

Esto asegura que cuando código C++ incluye la cabecera, los símbolos en su partido fichero objeto del contenido de la biblioteca C. Solo debe colocar extern "C" {} alrededor de su encabezado C si es antiguo y ya no tiene estas protecciones.

+2

¡algunos ejemplos breves son realmente buenos! – zhy

6

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

Por ejemplo, si tiene un proyecto con 3 archivos, util.c, util.h, main.cpp y ambos los archivos .c y .cpp se compilan con el compilador de C++ (g ++, cc, etc.), entonces no es realmente necesario, e incluso puede provocar errores de enlazador. Si su proceso de compilación usa un compilador de C normal para util.c, necesitará usar extern "C" cuando incluya util.h.

Lo que sucede 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 sucederle a una función C es la adición de un guión bajo ("_") al comienzo del nombre. Sin usar la opción "C", el vinculador buscará una función llamada DoSomething @@ int @ float() cuando el nombre real de la función sea _DoSomething() o simplemente DoSomething().

Usando extern "C" resuelve el problema anterior diciendo al compilador de C++ que debe buscar una función que siga la convención de nomenclatura de C en lugar de la de C++.