¿Cuándo poner definiciones de funciones estáticas en archivos de encabezado en C?

7 minutos de lectura

Me encontré con un código que tiene una gran función estática en un archivo de encabezado y tengo curiosidad por saber cuándo está bien/no está bien hacer esto. Por ejemplo, si muchos .c los archivos incluyen el encabezado, ¿por qué no simplemente definir la función como no estática y vincularla?

Se agradecería cualquier consejo o regla general sobre cuándo/cuándo no colocar definiciones de funciones estáticas en archivos de encabezado en C,

Gracias

  • Solo uso funciones estáticas en archivos de encabezado para programar microcontroladores simples. En este entorno, solo se ejecuta un programa único en el chip (sin sistema operativo), y las funciones incluidas también tienden a ser muy simples, como enviar datos a través del puerto serie, por ejemplo. No sé si esta es la respuesta CORRECTA (me encantaría escuchar algunos comentarios si no es así), pero me simplifica las cosas y no veo ningún inconveniente para esta aplicación en particular.

    – Miguel

    10 de marzo de 2015 a las 14:43

avatar de usuario
R.. GitHub DEJAR DE AYUDAR A ICE

Algunas ideas:

  • Un posible uso legítimo en el que puedo pensar es cuando desea que una función esté disponible sin crear un símbolo con enlace externo y contaminar el espacio de nombres externo. (Pero entonces podría usar un nombre prefijado oscuro como mylib123__foobary #define foobar mylib123__foobar en el archivo de encabezado, por lo que este parece un poco dudoso).
  • Desea que cierta funcionalidad esté disponible únicamente a través del archivo de encabezado, sin requerir que el usuario vincule una biblioteca/archivos de objetos. Pude ver que esto es una motivación real al proporcionar una ‘biblioteca’ que no es más que estructuras de datos y algunas piezas de código triviales para manipularlas. De hecho, si las estructuras de datos no son opacas y están destinadas a ser accedidas directamente por la aplicación, colocar funciones para usar con ellas en el mismo archivo de encabezado (en lugar de en una biblioteca) reduce en gran medida el riesgo de romper cosas si / cuando cambia los datos estructuras
  • Quizás la función es simplemente un contenedor para una función externa, y la forma en que funciona el contenedor puede depender de las opciones de tiempo de compilación en la unidad de compilación que llama. Por ejemplo:

    static int foobar(int x)
    {
        return real_foobar(COMPILETIME_PARAMETER, x);
    }
    

    Podría decir que solo use macros, pero ¿y si foobar necesita ser llamado a través de un puntero de función para el uso previsto?

Dicho esto…

En realidad, la razón principal por la que la gente pone static Las funciones en los archivos de encabezado generalmente se basan en una noción obsoleta de 10 años de que mejorará el rendimiento, al permitir que el compilador alinee la función o lo que sea. La mayoría de las personas que hacen esto no han hecho ninguna medición. Dado que los compiladores modernos pueden compilar todo el programa como una unidad si se les solicita, y esto teóricamente da como resultado muchas más posibilidades de optimización, y dado que, para empezar, es una optimización cuestionable, soy realmente escéptico sobre la ubicación de las funciones en los encabezados con fines de rendimiento. .

Esta crítica se aplica especialmente al ejemplo del OP de funciones estáticas “grandes” en archivos de encabezado. Casi no hay forma de que una función grande pueda beneficiarse de la inserción a menos que un valor de argumento constante permita al compilador eliminar el 90% del código o algo así. (Para ver un ejemplo del mundo real de este caso extremo, consulte algunas de las locas definiciones de macros/funciones en línea utilizadas en libavcodec. 🙂

Como regla general, no debe colocar funciones estáticas en los archivos de encabezado. En un programa único, probablemente no hará daño, además de expandir el tamaño de su código porque tiene una copia redundante en cada módulo. En una biblioteca compartida, podría causar errores fácilmente porque ahora parte de su biblioteca está incrustada en los llamantes de la biblioteca, por lo que fácilmente podrían ocurrir discrepancias de versión.

Si tiene una función terriblemente crítica en el tiempo donde el tiempo dedicado a hacer la llamada de función es importante, podría considerar ponerlo en el encabezado, pero en ese caso (a) probablemente también desee declararlo en línea, y (b) ya ha realizado todas las demás optimizaciones que puede encontrar.

En resumen, a menos que sepa sin lugar a dudas que necesita su función estática en un archivo de encabezado… no desea una función estática en un archivo de encabezado; desea una función no estática en un archivo .c con su encabezado en .h.

avatar de usuario
tobyodavies

En mi experiencia, generalmente es una mala idea definir una función en un archivo .h, y nunca he tenido motivos para hacerlo, hacerlo por accidente una vez me causó un sinfín de dolores de cabeza.

Aunque supongo que permitiría que cada archivo que incluye el encabezado tenga su propio separar implementación de la función que, si la función tiene variables estáticas, puede ser el comportamiento deseado, por ejemplo, si desea/necesita realizar un seguimiento de alguna información por separado para cada archivo.

  • Creo que tu segundo párrafo va por buen camino. Estoy de acuerdo en que si puede hacer que la función sea reentrante (por ejemplo, asignando y luego pasando un puntero a una estructura de estado), esa sería una solución mucho mejor.

    – Mateo Flaschen

    18 de octubre de 2010 a las 5:15

El C moderno ha adoptado el inline palabra clave de C++ para tal tarea. Pero si su compilador no tiene eso (¿todavía?) static en los archivos de encabezado es una forma de emular eso. inline no significa que la función esté necesariamente en línea para cualquier persona que llama, sino que, por lo general, habrá como máximo una copia en el ejecutable final. (Técnicamente, los símbolos de enlace correspondientes son símbolos “débiles”). Por el contrario, si solo se declaran static cada unidad de compilación guardará una copia.

Tal enfoque de tener definiciones de funciones en los encabezados debe restringirse a pequeña funciones que realizan pequeñas tareas para las cuales su compilador puede mejorar sustancialmente el código si está optimizado en la función de llamada.

Al hacerlo, también tenga cuidado con la implementación de estas funciones. Puede romper su posibilidad de incluir declaraciones en C++ por eso. En general, los dos idiomas solo están de acuerdo (en su mayoría) en las interfaces, no necesariamente para la implementación, existen diferencias sutiles.

También puede ser útil para definir funciones con búfer de trabajo estático para que sean locales para cada unidad de traducción. Un ejemplo particular es strtok(). strtok() marcha a través de un búfer un token por llamada. Si las llamadas strtok() se intercalan desde dos lugares diferentes (es decir, dos unidades de traducción diferentes), los resultados no son los esperados/deseados. Si cada unidad de traducción tuviera su propia copia de strtok() y, por lo tanto, cada unidad de traducción tuviera sus propias variables estáticas de strtok(), entonces este tipo de pisoteo en el estado interno desaparecería. Si está ocurriendo una pisada de estado, entonces ambas (conjuntos de) llamadas están en la misma unidad de traducción y la depuración tiene cierta apariencia de localidad.

(Tenga en cuenta que la solución “correcta” es reemplazar strtok() con una función sin estado y hacer que las personas que llaman sean responsables de mantener el contexto y la información del estado, de la misma manera que fopen() y sus amigos hacen que la persona que llama tenga un ARCHIVO para cada contexto).

Si la función tiene un enlace externo, debe declararse en un archivo .h.

Si la función es estática y, por lo tanto, no tiene vínculos externos, la función solo debe declararse en el archivo .c en el que está definida.

Nunca está bien tener una función definida en un archivo de encabezado.

¿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