how from code c++ c compiler-construction linker calling-convention

from - ¿Cuáles son las diferencias entre las convenciones de llamadas C y C++?



extern c (5)

Como sé, la convención de llamadas depende del compilador y de la arquitectura.

Sí.

Pero, ¿hay alguna diferencia clara entre las convenciones de llamadas C y C ++?

La mejor pregunta es "¿Hay similitudes?"

Sí:

Una función declarada extern "C" dentro de una aplicación C ++ tiene la misma convención de llamada utilizada por una función C en esa arquitectura.

Cualquier otra suposición que haga sobre las convenciones de llamadas es especulativa y puede ser la misma, pero debe preocuparse por las bases caso por caso.

Como sé, la convención de llamadas depende del compilador y de la arquitectura. Pero, ¿hay alguna diferencia clara entre las convenciones de llamadas C y C ++?


Pero, ¿hay alguna diferencia clara entre las convenciones de llamadas C y C ++?

En general, no hay ninguno. C ++ fue diseñado intencionalmente para ser lo más compatible posible con C, y en particular utiliza la interfaz binaria de la aplicación C en todos los sistemas.

Pero el C ABI no cubre muchas de las funciones que C ++ necesita (en particular, sobrecarga, espacios de nombres y plantillas de funciones), por lo que el compilador de C ++ realiza algunos cambios en los nombres de las funciones. Esto se llama manipulación de nombre

Entonces, para que funcionen las llamadas de función entre C y C ++, dichas funciones deben declararse como extern "C" que deshabilita el mangle de nombres y se asegura de que las convenciones de llamadas sean las esperadas por C (pero espero que este último aspecto sea automático , aunque el estándar no lo exige).

C ++ también tiene convenciones de llamadas adicionales para funciones miembro (a veces llamadas thiscall ) que no existen en C. Pero las funciones gratuitas usan la misma convención de llamadas que C, cualquiera que sea eso en un sistema dado.


No hay nada en ninguno de los estándares que requiera que las convenciones de llamadas C y C ++ sean las mismas en un compilador dado, aparte de que una función C ++ declarada extern "C" necesariamente debe llamarse con la misma convención de llamadas que una función C.

Es por eso que un puntero a la función y un puntero a la función con la vinculación C con los mismos parámetros y el tipo de retorno, tienen diferentes tipos. Cuando se llama a la función, el compilador puede saber del tipo con el que se llama a la convención para llamar, si son diferentes.

En la práctica , no creo que haya tratado a sabiendas un ejemplo que usa una convención de llamadas incompatible entre funciones libres con y sin enlace C. Por lo general, una implementación de C ++ adopta su convención de llamadas del ABI del sistema en el que planea ejecutarse, para producir objetos vinculables (ejecutables y bibliotecas) que otros usuarios del sistema puedan entender en términos de ABI.

Esto no es necesario: al estándar no le importa si hay un ABI del sistema, y ​​al sistema generalmente no le importa cómo se hacen las llamadas dentro de un ejecutable autónomo [*]. Es sensato hacerlo a menos que exista alguna razón extraordinaria para no hacerlo. El sistema ABI en un sistema dado puede o no mencionar a C ++; si no, entonces la implementación de C ++ es por sí misma en lo que respecta a las funciones que no son de vinculación C, pero como digo es usualmente sensato hacer funciones que podrían tienen el enlace C usan la misma convención de llamadas como si tuvieran un enlace C.

Digo "incompatible" en lugar de "diferente", porque, por supuesto, hay algunas cosas que no se mencionan en la convención de llamadas de C, pero que deben especificarse en la convención de llamadas de C ++. Cómo pasar una función de puntero a miembro, por ejemplo. Es muy posible que esto no esté delimitado por el sistema ABI, y por lo tanto queda a la implementación de C ++. La razón para esto es dejar la implementación de la función de puntero a miembro hasta la implementación de C ++, en lugar de que el sistema ABI haga algo que el fabricante considere que es el trabajo de un escritor de compiladores de C ++.

Con la convención de llamadas fuera del camino, tenga en cuenta que el nombre de "mangle" inevitablemente es diferente entre una función libre con o sin enlace C. La razón es que el manchado de nombres de C ++ debe incluir los tipos de los parámetros (debido a la sobrecarga de funciones), mientras que el mangle de nombres de C no debe (debido a las declaraciones de funciones externas con parámetros no especificados).

[*] Aunque he visto ejemplos en los que el uso de la convención de llamadas "incorrectas" rompe las cosas. ISTR es un dispositivo móvil de Windows donde si formateó la pila de forma diferente a lo que esperaba el sistema operativo, entonces ciertas excepciones de hardware eliminarían el dispositivo porque el sistema operativo intentó volver sobre la pila del hilo ofensivo y no pudo. Al menos en esa versión del sistema operativo, probablemente alrededor de 2005, si quería que los diagnósticos del sistema operativo funcionaran, tenía que usar las convenciones de llamadas de Windows internamente. O de todos modos la parte de la convención de llamadas relacionada con el formato de marco de pila. Esto fue totalmente nuestro error, por supuesto, pero no sé si podríamos haberlo arreglado correctamente al instalar nuestros propios controladores para las excepciones de hardware, en lugar de trabajar en torno a ellos al no causar las excepciones en primer lugar. Significaba que un proceso en modo de usuario podría trivialmente tomar el sistema operativo con una sobrecarga de pila, y también que era más difícil depurar nuestro código que en otros Windows. Así que culpamos ligeramente al sistema operativo.


Toda la noción de las distintas convenciones de llamada entre idiomas va más allá de cualquier idioma (y su especificación). Y así es como debería ser, esas cosas no tienen importancia para la especificación de ningún idioma que valga su nombre. Básicamente, tienes razón, está en el dominio de una implementación específica de una especificación de la arquitectura del compilador. Claro, algunas nociones discutidas en la especificación / estándar de un lenguaje dado como la especificación de vinculación pueden afectar la convención de llamadas, pero no es una consecuencia planificada .

El deber de ABI es estandarizar / formalizar tales conceptos que, una vez más, la gente no tiene obligación de respetar (y probablemente porque la paleta de colores de las implementaciones varía bastante)

Realmente, la especificación debe ser ajena a cómo se transmiten los parámetros, ya sea terminando en la pila o en los registros, una combinación de ambos, ordenando la asignación, etc. Ese es el trabajo de los muchachos que trabajan en la implementación y las personas que diseñan el hardware real, más importante aún, los conjuntos de instrucciones.

Por lo tanto: ni C ni C ++, como estandarizados, tienen voz ni voto en cómo se implementan las cosas. Por lo tanto, no debe haber ninguna diferencia inherente a los idiomas. Solo en la forma en que se aplican. Y ese es el campo de la arquitectura del compilador.


Ningún estándar C o C ++ define un ABI. Esta es una decisión de diseño intencional que permite a los escritores de compiladores la libertad de crear una implementación lo más eficiente posible.

Con el tiempo, una convención de llamadas C llamada cdecl se ha convertido en el estándar de facto de las arquitecturas x86. Existen estándares de facto similares para el código C que se ejecuta en las máquinas AMD64, pero difieren entre los sistemas operativos UNIX y Windows.

En máquinas Unix, hay un cierto movimiento hacia un ABI común de C ++ basado en el Itanium C ++ ABI publicado por Intel. Sin embargo, debido a la complejidad adicional de C ++, las diferentes versiones del mismo compilador e incluso los diferentes modificadores de compilación a menudo producen códigos de máquina que se ajustan a los ABI incompatibles.

El uso de la extern "C" { ... } menudo se puede confiar para forzar a un compilador a implementar una función usando el C ABI de facto, pero incluso esto no es requerido por el estándar de C ++.

Si está interesado en una descripción exhaustiva de los ABI actuales de C ++, entonces debería consultar las "Convenciones de llamadas de Agner Fog para diferentes compiladores de C ++ y sistemas operativos" .