2010-03-08 13 views
20

Estoy tomando un curso de lenguajes de programación y estamos hablando de la declaración extern "C".¿Cómo funciona una declaración "C" externa?

¿Cómo funciona esta declaración a un nivel más profundo que no sea "interfaces C y C++"? ¿Cómo afecta esto a las vinculaciones que tienen lugar en el programa también?

Respuesta

41

extern "C" se utiliza para asegurar que los símbolos siguientes son no mangled (decorada).


Ejemplo:

Digamos que tenemos el siguiente código en un archivo llamado test.cpp:

extern "C" { 
    int foo() { 
    return 1; 
    } 
} 

int bar() { 
    return 1; 
} 

Si ejecuta gcc -c test.cpp -o test.o

Tome un vistazo a los nombres de símbolos :

00000010 T _Z3barv

00000000 T foo

foo() mantiene su nombre.

+8

También esas funciones usan la convención de llamadas C, si difiere de la convención de llamadas C++. Lo cual probablemente no sea así, para las funciones de C++ que toman solo los tipos de C como parámetros, pero con 'extern' C '' definitivamente no. –

+0

¿Qué llamada usaste para obtener los nombres de los símbolos allí? – meawoppl

+2

@meawoppl ['nm'] (http://linux.die.net/man/1/nm). –

-2

Aquí es una cita de MSDN

"La palabra clave extern declara una variable o función y especifica que tiene enlazado externo (su nombre es visible desde archivos distintos de aquel en el que se define). Cuando la modificación de una variable, extern especifica que la variable tiene una duración estática (se asigna cuando el programa comienza y se desasigna cuando termina el programa). La variable o función se puede definir en otro archivo fuente, o más adelante en el mismo archivo. Declaraciones de variables y funciones en el alcance del archivo son externos por defecto ".

http://msdn.microsoft.com/en-us/library/0603949d%28VS.80%29.aspx

+4

que no describe lo que 'extern "C"' hace en absoluto ... –

+0

Es una cita de Microsoft, y la forma en que se ha utilizado durante años, me va a tocar con una cita de Microsoft, le sugiero que vaya a tomar la clase –

+0

, derecha, Microsoft, los pioneros mundialmente famosos de documentación fácil de entender. Esa cita es técnicamente correcta, pero inútil para un principiante. –

9

Cuando marca un bloque de código con extern "C", le está diciendo al sistema que use el enlace de estilo C.

Esto, principalmente, afecta la forma en que el enlazador destruye los nombres. En lugar de utilizar el mangling del nombre de estilo de C++ (que es más complejo para soportar sobrecargas del operador), obtienes la nomenclatura del estilo C estándar del enlazador.

+1

No creo que deba sugerir que un ** método ** podría marcarse con 'extern' C "'. Las funciones individuales o múltiples funciones están bien. – quamrana

+0

@Reed: err .. Me acabo de dar cuenta de que hablas de enlaces y enlazadores. Afecta el nombre del compilador, que solo afecta indirectamente al enlazador. – quamrana

+0

@quamrana: lea http://msdn.microsoft.com/en-us/library/0603949d(VS.80).aspx: afecta directamente y lo usa el vinculador. El compilador destroza el nombre, pero el enlazador es el que USA el nombre: Extern "C" está ahí para permitir que el enlazador funcione correctamente, ya sea en C o en C++, ya que el enlazador es el que debe determinar cómo mapear entre unidades de traducción. –

5

En C++, el nombre/símbolo de las funciones se renombra en realidad a otra cosa, de modo que diferentes clases/espacios de nombres pueden tener funciones de las mismas firmas. En C, las funciones están definidas globalmente y no se necesita un proceso de cambio de nombre personalizado.

Para que C++ y C hablen entre sí, "extern C" indica al compilador que no use la convención C.

+4

para obtener más información sobre el nombre de manipulación, hay Wikipedia: http://en.wikipedia.org/wiki/Name_mangling – jakebman

+0

@jakebman: Ese es un gran artículo. ¡Gracias! – gilbertc

+0

'"extern C" indica al compilador que no use la convención C' No, eso está al revés. "extern C" instruye a un compilador de C++ a usar la convención de C para nombrar símbolos. – JonN

21

Veamos una función típica que se puede compilar en C y C++:

int Add (int a, int b) 
{ 
    return a+b; 
} 

Ahora en C la función se llama "_ADD" internamente. Mientras que la función C++ se llama algo completamente diferente internamente usando un sistema llamado mangle. Básicamente es una forma de nombrar una función para que la misma función con diferentes parámetros tenga un nombre interno diferente.

Así que si Add() se define en add.c, y tiene el prototipo en add.h, obtendrá un problema si intenta incluir add.h en un archivo C++. Como el código de C++ busca una función con un nombre diferente al de add.c, obtendrá un error de enlazador. Para conseguir alrededor de ese problema debe incluir add.c por este método:

extern "C" 
{ 
#include "add.h" 
} 

Ahora el código C++ enlazará con _ADD en lugar del nombre de la versión C++ destrozado.

Ese es uno de los usos de la expresión. En pocas palabras, si necesita compilar un código que es estrictamente C en un programa C++ (a través de una instrucción include u otro medio), debe envolverlo con una declaración externa "C" {...}.

+2

El '_' inicial es dependiente del compilador/enlazador/sistema. –

+0

Ok, pero nunca me he encontrado con un compilador de C que no lo haga, incluso desde los días de Amiga :) – Cthutu

+3

La mejor respuesta aquí. Se merece más votos. – azizbekian

3

extern "C" indica que el código cerrado utiliza la vinculación de estilo C y renombrado de nombres. C++ usa un formato de creación de nombres más complejo. He aquí un ejemplo:

http://en.wikipedia.org/wiki/Name_mangling

int example(int alpha, char beta); 

en C: _example

en C++: __Z7exampleic

Actualización: Como GManNickG señala en los comentarios, el patrón de renombrado de nombres es compilador dependiente.

+1

Tenga en cuenta que no existe un solo nombre que se debañe, todo depende de la implementación. – GManNickG

4

extern C afecta a nombre mangling por el compilador de C++. Es una forma de hacer que el compilador de C++ no destruya los nombres, o más bien que los manipule del mismo modo que un compilador de C. Esta es la forma en que las interfaces C y C++.

Como un ejemplo:

extern "C" void foo(int i); 

permitirá la función que se implementa en un módulo de C, pero permitir que se llama desde un módulo de C++.

El problema viene cuando se trata de obtener un módulo C para llamar a una función de C++ (C, obviamente, no puede utilizar clases de C++) definida en un módulo de C++. El compilador C no le gusta extern "C".

por lo que necesita para utilizar este:

#ifdef __cplusplus 
extern "C" { 
#endif 

void foo(int i); 

#ifdef __cplusplus 
} 
#endif 

Ahora, cuando esta aparece en un archivo de cabecera, tanto los compiladores de C y C++ estarán contentos con la declaración y ahora podría ser definido en cualquiera de una C o Módulo C++, y puede ser llamado por ambos códigos C y C++.

5

Cabe señalar que extern "C" también modifica los tipos de funciones. No sólo modificar las cosas en los niveles más bajos:

extern "C" typedef void (*function_ptr_t)(); 

void foo(); 

int main() { function_ptr_t fptr = &foo; } // error! 

El tipo de &foo No es igual el tipo que designa los typedef (aunque el código es aceptado por algunos, pero no todos los compiladores).

0

extern "C", es una palabra clave para declarar una función con fijaciones C, porque el compilador de C y C++ Compiler se traducirán de origen en forma diferente en el archivo de objeto:

Por ejemplo, un fragmento de código es el siguiente:

int _cdecl func1(void) {return 0} 
int _stdcall func2(int) {return 0} 
int _fastcall func3(void) {return 1} 

compiladores de 32 bits C traducirán el código en la forma como sigue:

_func1 
[email protected] 
@[email protected] 

en el cdecl, func1 se traducirá como '_name '

en el stdcall, func2 se traducirá como' _name @ X '

en el fastcall, func2 se traducirá como' @ nombre @ X '

' X 'significa la cantidad de bytes de los parámetros en la lista de parámetros.

convención de 64 bits en Windows no tiene ningún subrayado inicial

En C++, se introdujo clases, plantillas, espacios de nombres y la sobrecarga de operadores, ya que no se permite dos funciones con el mismo nombre, compilador de C++ proporcionar la información de tipo en el nombre del símbolo,

por ejemplo, un fragmento de código es el siguiente:

int func(void) {return 1;} 
int func(int) {return 0;} 
int func_call(void) {int m=func(), n=func(0);} 

compilador de C++ traducir el código de la siguiente manera:

int func_v(void) {return 1;} 
int func_i(int) {return 0;} 
int func_call(void) {int m=_func_v(), n=_func_i(0);} 

'_v' y '' son _i información de tipo de 'vacío' y 'int'