¿Por qué necesitamos una “C” externa{ #include } en C++?

8 minutos de lectura

¿Por que necesitamos una C externa include en C
Aterrizar

¿Por qué necesitamos usar:

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

Específicamente:

  • ¿Cuándo debemos usarlo?

  • ¿Qué está sucediendo en el nivel del compilador/enlazador que requiere que lo usemos?

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

1646961968 441 ¿Por que necesitamos una C externa include en C
duane

C y C++ son superficialmente similares, pero cada uno se compila en un conjunto de código muy diferente. Cuando incluye un archivo de encabezado con un compilador de C++, el compilador espera código de C++. Sin embargo, si se trata de un encabezado C, entonces el compilador espera que los datos contenidos en el archivo de encabezado se compilen en un formato determinado: el ‘ABI’ de C++ o ‘Interfaz binaria de la aplicación’, por lo que el enlazador se atraganta. Esto es preferible a pasar datos de C++ a una función que espera datos de C.

(Para entrar en el meollo del asunto, la ABI de C++ generalmente ‘destroza’ los nombres de sus funciones/métodos, llamando así printf() sin marcar el prototipo como una función de C, C++ en realidad generará una llamada de código _Zprintfademás de basura adicional al final).

Entonces: usa extern "C" {...} cuando se incluye el encabezado ac, es así de simple. De lo contrario, tendrá una discrepancia en el código compilado y el enlazador se ahogará. Sin embargo, para la mayoría de los encabezados, ni siquiera necesitará el extern porque la mayoría de los encabezados del sistema C ya tendrán en cuenta el hecho de que pueden estar incluidos en el código C++ y ya extern "C" su código.

  • ¿Podría por favor elaborar más sobre “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 externen su código”.?

    – Bulat M.

    28/09/2016 a las 18:39

  • @BulatM. Contienen algo como esto: #ifdef __cplusplus extern "C" { #endif Entonces, cuando se incluyen desde un archivo C ++, todavía se tratan como un encabezado C.

    – Calmarius

    13 de marzo de 2017 a las 12:58

¿Por que necesitamos una C externa include en C
Todd Gamblin

extern “C” determina cómo se deben nombrar los símbolos en el archivo de objeto generado. Si una función se declara sin “C” externa, el nombre del símbolo en el archivo de objeto utilizará la manipulación de nombres de C++. Aquí hay un ejemplo.

Dado test.C así:

void foo() { }

Compilar y enumerar 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 en realidad se llama “_Z3foov”. Esta cadena contiene información de tipo para el tipo de devolución y los parámetros, entre otras cosas. Si en cambio escribes test.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

Obtienes 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 la manipulación de nombres.

Por lo general, incluye un encabezado dentro de “C” externo {} si el código que lo acompaña se compiló con un compilador de C, pero intenta llamarlo desde C++. Cuando hace esto, le está 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”, que con suerte coincide con lo que esté en la biblioteca con la que está vinculando.

La mayoría de las bibliotecas modernas colocarán protectores alrededor de dichos 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 coinciden con lo que hay en la biblioteca C. Solo debe colocar una “C” externa {} alrededor de su encabezado C si es antiguo y aún no tiene estos protectores.

1646961969 689 ¿Por que necesitamos una C externa include en C
Trento

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

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

Para diferenciarlos a todos, el compilador de C++ creará nombres únicos para cada uno en un proceso llamado modificación de nombres o decoración. 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 compilador de C++ que no realice ningún cambio de nombre en el código entre llaves. Esto le permite llamar a funciones C desde dentro de C++.

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

Para resolver esto, le decimos al compilador de C++ que se ejecute en modo “C”, por lo que realiza la manipulació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.

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 de objeto producido por el compilador es una referencia a esa función que llamó “openBankAccount” en otro archivo de objeto producido a partir de 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 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. Entonces, el entero “calcetines” se almacena como “calcetines” y la función contar_calcetines se almacena como “contar_calcetines”.

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

Pero en C++, el lenguaje te 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 “agregar” deben tener símbolos diferentes, o el incorrecto será utilizado por otros archivos de objetos. Esto se hace “destrozando” (ese es un término técnico) los nombres de los símbolos.

Al vincular código C++ a bibliotecas o código C, necesita cualquier cosa “C” externa escrita en C, como archivos de encabezado para las bibliotecas C, para decirle a su compilador C++ que estos nombres de símbolos no se deben alterar, mientras que el resto de su código C ++, por supuesto, debe estar destrozado o no funcionará.

1646961970 11 ¿Por que necesitamos una C externa include en C
tony m

¿Cuándo debemos usarlo?

Cuando está vinculando bibliotecas C en archivos de objetos C ++

¿Qué está sucediendo en el nivel del compilador/enlazador que requiere que lo usemos?

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 esto resuelve los problemas que requieren que lo usemos?

El uso del esquema de nombres C 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.

Debe usar “C” externo cada vez que incluya un encabezado que defina funciones que residan en un archivo compilado por un compilador de C, usado en un archivo de C++. (Muchas bibliotecas C estándar pueden incluir esta verificación en sus encabezados para que sea más simple para el desarrollador)

Por ejemplo, si tiene un proyecto con 3 archivos, util.c, util.h y main.cpp y los archivos .c y .cpp se compilan con el compilador de C++ (g++, cc, etc.), entonces no es No es realmente necesario, e incluso puede causar errores en el enlazador. Si su proceso de compilación usa un compilador C regular para util.c, entonces necesitará usar “C” externo 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 principio del nombre. Sin usar la “C” externa, el enlazador buscará una función llamada DoSomething@@int@float() cuando el nombre real de la función es _DoSomething() o simplemente DoSomething().

El uso de “C” externo resuelve el problema anterior diciéndole 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++.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad