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

Me encontré con un código que tiene una gran función estática en un archivo de cabecera y solo tengo curiosidad cuando está / no está bien para hacer esto. Por ejemplo, si muchos archivos .c incluyen el encabezado, ¿por qué no solo define la función no estática y la vincula?

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

Gracias

Algunas ideas:

  • Un posible uso legítimo que se me ocurre es cuando desea que una función esté disponible sin crear un símbolo con enlaces externos y contaminando el espacio de nombres externo. (Pero entonces podrías simplemente usar un nombre prefijado oscuro como mylib123__foobar , y #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 que el usuario tenga que vincular archivos de biblioteca / objeto. Pude ver que esto es una verdadera motivación cuando proporciono una ‘biblioteca’ que no es más que estructuras de datos y algunas piezas triviales de código para manipularlas. De hecho, si las estructuras de datos no son opacas y la aplicación debe acceder directamente a ellas, colocar funciones para usarlas en el mismo archivo de encabezado (versus en una biblioteca) reduce en gran medida el riesgo de romper cosas si / cuando cambia los datos estructuras.
  • Quizás la función sea simplemente una envoltura para una función externa, y la forma en que funciona la envoltura puede depender de las opciones de tiempo de comstackción en la unidad de comstackción de llamadas. Por ejemplo:

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

    Se podría decir que solo se usan macros, pero ¿qué foobar si se necesita llamar a foobar mediante un puntero de función para el uso previsto?

Habiendo dicho eso …

En realidad, la razón principal por la que las personas colocan funciones static en los archivos de cabecera suele basarse en una noción obsoleta de hace 10 años de que mejorará el rendimiento al permitir que el comstackdor incorpore 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 comstackdores modernos pueden comstackr todo el progtwig como una unidad si se lo solicitan, y esto teóricamente genera muchas más posibilidades de optimización, y dado que es una optimización cuestionable para empezar, soy muy escéptico sobre la colocación de funciones en los encabezados para fines de rendimiento. .

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

Como regla general, no debe colocar funciones estáticas en los archivos de encabezado. En un progtwig único, probablemente no le hará daño a nada, además de ampliar 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á integrada en las llamadas de la biblioteca, por lo que las discrepancias de versión podrían ocurrir fácilmente.

Si tiene alguna función terrible, terriblemente crítica en la que importa el tiempo dedicado a llamar a la función, podría considerar ponerla en el encabezado, pero en ese caso (a) probablemente también desee declararla 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 quiere 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.

En mi experiencia, generalmente es una mala idea definir una función en un archivo .h, y nunca tuve la oportunidad de hacerlo, hacerlo por accidente una vez me causó innumerables dolores de cabeza.

Aunque supongo que permitiría que cada archivo que incluye el encabezado tenga su propia implementación separada de la función que, si la función tiene vars estáticos, puede ser el comportamiento deseado, por ejemplo, si desea / necesita realizar un seguimiento de cierta información por separado para cada uno archivo.

También puede ser útil para definir funciones con búferes de trabajo estáticos para que sean locales para cada unidad de traducción. Un ejemplo particular es strtok (). strtok () marcha a través de un buffer un token por llamada. Si las llamadas strtok () se intercalan desde dos lugares diferentes (es decir, dos unidades de traducción diferentes), entonces 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 strtok (), este tipo de pisadas en el estado interno desaparecería. Si está ocurriendo pisada de estado, ambas (series de) llamadas están en la misma unidad de traducción y la depuración tiene una apariencia de localidad.

(Tenga en cuenta que la solución “correcta” es reemplazar strtok () con una función sin estado y hacer que los llamadores sean responsables de mantener la información de contexto y estado, del mismo modo que fopen () y sus amigos hacen que la persona que llama contenga un ARCHIVO para cada contexto).

Modern C ha adoptado la palabra clave en inline de C ++ para dicha tarea. Pero si su comstackdor no tiene esa (¿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 solo que habrá como máximo una copia en el ejecutable final. (Técnicamente, los símbolos del vinculador correspondientes son símbolos “débiles”). Por el contrario, si se declara static cada unidad de comstackción guardará una copia.

Este enfoque de tener definiciones de funciones en los encabezados debe restringirse a las funciones pequeñas que realizan tareas pequeñas para las cuales su comstackdor puede mejorar sustancialmente el código si está optimizado para 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 coinciden (principalmente) en interfaces, no necesariamente para la implementación, existen diferencias sutiles.

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 enlaces 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.