¿Son las variables 'estáticas' de alcance de archivo en C tan malas como las variables globales 'externas'?

8

En C, a menudo / a veces (como cuestión de estilo) usa una staticvariable de alcance de archivo donde usaría una variable miembro de clase privada en C ++. Al escalar a programas multiproceso, simplemente agregar thread_localC11 o la extensión de larga duración se __threadadapta bien. Sé que puedes hacer exactamente lo mismo en C que en C ++ poniendo todo dentro de ay structhaciendo un conjunto de funciones que tome un puntero a eso structcomo primer argumento. Algunas bibliotecas hacen esto ampliamente. Pero mi estilo personal es mantener lo structmás pequeño posible, si es necesario.

A menudo leo o escucho a algunas personas argumentando que las variables 'globales' son muy malas. Sigo sus razones, y la mayoría de sus argumentos parecen estar relacionados con externvariables globales en términos de C. Lo que dicen es ciertamente cierto. A veces uso 1 o 2 de las externvariables declaradas en todo el programa cuando simplificará mucho las cosas y cuando es fácil hacer un seguimiento de ellas, pero ir más allá fácilmente hará que un programa sea impredecible.

¿Qué pasa con las staticvariables? ¿Siguen teniendo el mismo problema que las variables globales "reales"? Tal vez ni siquiera tengo que hacer esta pregunta y continuar si creo que lo que estoy haciendo es correcto, pero hoy vi otro tipo de publicación 'Las variables globales son MALAS', y finalmente vine aquí pensando que tal vez sea un derecho lugar para tal tipo de pregunta. Cual es tu pensamiento

Esta pregunta no es un duplicado de esto porque esta pregunta hace referencia a variables locales externy staticno locales, mientras que la otra pregunta trata sobre staticvariables de alcance de archivo y alcance de bloque .

xiver77
fuente
3
No creo que esto sea un duplicado. Esta pregunta es acerca de una variable estática de archivo en comparación con una variable estática externa (global). La otra pregunta es comparar las variables estáticas de función con las variables globales.
@gnat Esta no es una pregunta duplicada. Hice una edición para explicar que básicamente dice lo que dijo Snowman en su comentario. Incluso el título es diferente.
xiver77
@ xiver77, aunque actualmente no hay votos para cerrar como duplicados, algunas de las respuestas a la otra pregunta pueden ser interesantes aquí.
@Snowman por mi lectura, respuesta aceptada en esa otra cuestión cubre los tres casos (que parece explicar cuándo y por qué la estática ámbito de archivo son preferibles a la función tanto de ámbito y los globales)
mosquito

Respuestas:

17

En un programa C bien diseñado, una variable estática de archivo es similar a un miembro estático privado de una clase:

  • Solo se puede acceder mediante funciones en ese archivo, de manera similar a cómo una variable miembro estática privada solo se puede acceder mediante funciones en la clase en la que se define.

  • Solo hay una copia de la variable.

  • Su vida útil es la vida útil del programa.

Una externvariable sería una verdadera variable global como en cualquier idioma que las soporte.

Una staticvariable no global no es tan mala como una global; de hecho, son necesarios en algunos casos.

  • El acceso se controla a través de las funciones que usted escribe. Esto ayuda con la integridad de los datos, incluida la comprobación de límites y la seguridad de subprocesos. (nota: esto no garantiza la seguridad del hilo, es simplemente una herramienta para ayudar en el camino)

  • Los datos están encapsulados: solo ese archivo puede acceder a ellos. Esto es lo más cerca que C puede llegar a la encapsulación donde varias funciones pueden acceder a una variable estática.

Las variables globales son malas pase lo que pase. Las variables de archivo estático tienen los beneficios de una variable estática privada pero ninguno de los inconvenientes de una variable global.

El único problema es que, a diferencia de una verdadera variable estática privada, como en C ++, otros archivos pueden declarar una externvariable que coincida con la declaración y no puede evitar el acceso. En otras palabras, confía en el sistema de honor para evitar convertirlo en una variable global.


fuente
6

El estado global, incluidas las externvariables y las no const staticvariables en el alcance del archivo o en las funciones, con frecuencia puede ser una solución fácil a un problema dado, pero hay tres problemas:

  1. statichace que el código no sea comprobable , porque las staticvariables tienden a ser dependencias no reemplazables. O en otras palabras OOP-y: no estás siguiendo el Principio de Inversión de Dependencia. Llegué a C y C ++ desde lenguajes dinámicos como Perl, por lo que mi modelo de costos está inclinado hacia el despacho virtual y los punteros de función, etc. Con los lenguajes actuales, existe cierto conflicto entre la capacidad de prueba y la buena arquitectura, pero creo que la pequeña molestia de hacer explícitas sus dependencias y dejar que se anulen en las pruebas se compensa notablemente por la facilidad de escribir las pruebas, y así asegurarse de que su software funcione como esperado. Sin hacer que su código sea más dinámico, el único mecanismo disponible para inyectar dependencias para una prueba es la compilación condicional.

  2. El estado global hace que sea difícil razonar sobre la corrección , y eso conduce a errores. Mientras más partes tengan acceso a una variable y puedan modificarla, más fácil será perder la noción de lo que está sucediendo. En cambio: ¡prefiera la asignación única de variables! Prefiero constdonde sea razonable! Prefiere las variables de protección a través de getters y setters donde puedes introducir verificaciones de corrección. Mientras el estado sea staticy no extern, todavía es posiblepara mantener la corrección, pero siempre es mejor asumir que yo en una semana no será tan inteligente como yo en este momento. Especialmente en C ++, podemos usar clases para modelar varias abstracciones que hacen imposible el mal uso de algo, así que trate de utilizar el sistema de tipos en lugar de su inteligencia; tiene cosas más importantes en las que pensar.

  3. El estado global puede implicar que sus funciones no son reentrantes , o que solo pueden usarse en un contexto a la vez. ¡Imagine un controlador de base de datos que solo podría administrar una conexión! Esa es una restricción totalmente innecesaria. En realidad, las limitaciones son a menudo más sutiles, como una variable global que se utiliza para agregar resultados. En cambio, haga que su flujo de datos sea explícito y pase todo a través de los parámetros de la función. Nuevamente, las clases de C ++ pueden hacer que esto sea más manejable.

Obviamente, static const NAMED_CONSTANTSestán bien. Usar el staticinterior de las funciones es mucho más complicado: si bien es útil para las constantes inicializadas de forma perezosa, puede ser bastante indetectable. Un compromiso es separar el cálculo del valor inicial de la variable estática, de modo que ambas partes se puedan probar por separado.

En programas pequeños y autónomos, todo esto no importará, y puede seguir utilizando el staticestado para deleitar su corazón. Pero a medida que pasa alrededor de 500 LOC o si está escribiendo una biblioteca reutilizable, realmente debería comenzar a pensar en una buena arquitectura y una buena interfaz sin restricciones innecesarias.

amon
fuente
Algunas de sus sugerencias sobre diseño y seguridad a veces son imposibles de atender en programas que deben escribirse en C.
xiver77
Y el estado global no es necesariamente hostil. Tanto el último estándar C como C ++ tienen thread_localy los compiladores lo soportaron durante mucho tiempo como una extensión antes de la estandarización.
xiver77
Para evitar muchos de los problemas de estado global que mencionó, la mayoría de los programas de C bien escritos tienen una estructura superficial y transparente con dependencias externas minimizadas.
xiver77
Mantener el código estático y los datos mutables, aunque propensos a errores, es la única forma de escribir el programa más eficiente posible en la arquitectura actual de la computadora.
xiver77
@ xiver77 Tengo el lujo de escribir más código de alto nivel, por lo que no es necesario sacrificar la arquitectura por el rendimiento ni usar C en mi línea de trabajo :) Al escribir C, me resulta mucho más difícil escribir el código correcto, y la elección de abstracciones adecuadas es diferente de lo que preferiría, pero aún es posible proporcionar mucha seguridad. Mi respuesta trata de marcar claramente las ideas específicas de C ++ como tales. Su punto sobre los archivos pequeños es muy bueno, lo que facilita razonar sobre el programa y reduce, pero no elimina, el problema que plantean las staticvariables.
amon
1

No considero que las variables con alcance de archivo sean tan malas como las variables globales. Después de todo, todos los accesos a estas variables están confinados a un solo archivo fuente. Con esa restricción, las variables de alcance de archivo son casi tan buenas o malas como un miembro de datos estáticos privados de C ++, y no prohíbe su uso, ¿verdad?

cmaster - restablecer monica
fuente
1
En c ++, el tipo de sintaxis lo alienta a colocar muchas variables miembro en las clases, por lo que no es necesario tener un miembro estático privado. Pero en C, si no va a emular completamente lo que haría con una clase C ++, se pueden usar variables estáticas o thread_local en lugar de un miembro de estructura si quiere evitar pasar el puntero de estructura a todas partes. Mi pregunta es sobre esto.
xiver77
Estoy hablando de miembros con staticalmacenamiento, que existen exactamente una vez, no para cada objeto. Estas variables no necesitan ninguna instancia de la clase para que el código de la clase haga referencia a ellas, por lo que no es necesario pasar una referencia / puntero a un singleton. Esto es precisamente lo que las variables de alcance de archivo también logran. La única diferencia es que el staticmiembro tiene alcance de clase mientras que la staticvariable "global" tiene alcance de archivo. Pero estos dos ámbitos son muy similares en extensión, que es mi punto. Sin staticembargo, estoy de acuerdo en que los diferentes significados de son confusos.
cmaster - reinstalar a monica el
1
Al menos en C, estático realmente tiene un significado para las variables (no para las funciones).
xiver77
1

Todo está relacionado con el alcance de la variable (no constante, algo mutable) en mi opinión. Es una visión que ciertamente carece de matices, pero es un contador pragmático y un llamamiento para volver a los fundamentos más básicos para aquellos que dicen: "¡Esto es absolutamente malo!" solo para tropezar con problemas similares a los asociados con lo que critican, como las condiciones de carrera.

Imagine que tiene una función de línea de 50,000 con todo tipo de variables declaradas en la parte superior y gotodeclaraciones para saltar por todas partes. Eso no es muy agradable con un alcance de variables tan monstruoso, y tratar de razonar sobre la función y lo que está sucediendo con tales variables será extremadamente difícil. En un caso tan monstruoso, la distinción normal entre efecto secundario "externo" e "interno" pierde mucho de su propósito práctico.

Imagine que tiene un programa simple de 80 líneas que simplemente escribe una vez y crea una variable global (ya sea con enlace interno y alcance de archivo o enlace externo, pero de cualquier manera el programa es pequeño). Eso no es tan malo.

Imagine que tiene una clase monstruosa en un lenguaje orientado a objetos que contiene la lógica de todo su programa con miles y miles de líneas de código para su implementación. En ese caso, sus variables miembro son más problemáticas que las globales en el programa de 80 líneas anterior.

Si desea poder razonar mejor y con mayor confianza sobre su código, la seguridad del hilo (o la falta del mismo), hacerlo más predecible, asegurarse de que sus pruebas tengan una buena cobertura sin perder todo tipo de casos potenciales, etc. , entonces ayuda a limitar el acceso a las variables.

La estática del alcance del archivo tenderá a tener un alcance más estrecho que los que tienen un enlace externo, pero, de nuevo, si su archivo fuente tiene 100,000 líneas de código, todavía es bastante ancho. Entonces, para las estadísticas del alcance del archivo, si no puede evitarlas, trataría de mantener su alcance estrecho al no hacer que su archivo fuente pueda acceder a ellos de forma descomunal, ya que en ese caso reducir su alcance se trata de reducir el tamaño y el alcance del diseño del archivo fuente, en oposición a la función (para variables locales, incluidos los parámetros), clase (para variables miembro), o posiblemente módulo (para globales con enlace externo pero accesible solo dentro del módulo), o incluso software completo (para globales con enlace externo accesible a todo el software).

Dragon Energy
fuente