En C, a menudo / a veces (como cuestión de estilo) usa una static
variable de alcance de archivo donde usaría una variable miembro de clase privada en C ++. Al escalar a programas multiproceso, simplemente agregar thread_local
C11 o la extensión de larga duración se __thread
adapta bien. Sé que puedes hacer exactamente lo mismo en C que en C ++ poniendo todo dentro de ay struct
haciendo un conjunto de funciones que tome un puntero a eso struct
como primer argumento. Algunas bibliotecas hacen esto ampliamente. Pero mi estilo personal es mantener lo struct
má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 extern
variables globales en términos de C. Lo que dicen es ciertamente cierto. A veces uso 1 o 2 de las extern
variables 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 static
variables? ¿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 extern
y static
no locales, mientras que la otra pregunta trata sobre static
variables de alcance de archivo y alcance de bloque .
Respuestas:
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
extern
variable sería una verdadera variable global como en cualquier idioma que las soporte.Una
static
variable 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
extern
variable 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
El estado global, incluidas las
extern
variables y las noconst
static
variables 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:static
hace que el código no sea comprobable , porque lasstatic
variables 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.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
const
donde 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 seastatic
y noextern
, 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.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_CONSTANTS
están bien. Usar elstatic
interior 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
static
estado 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.fuente
thread_local
y los compiladores lo soportaron durante mucho tiempo como una extensión antes de la estandarización.static
variables.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?
fuente
static
almacenamiento, 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 elstatic
miembro tiene alcance de clase mientras que lastatic
variable "global" tiene alcance de archivo. Pero estos dos ámbitos son muy similares en extensión, que es mi punto. Sinstatic
embargo, estoy de acuerdo en que los diferentes significados de son confusos.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
goto
declaraciones 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).
fuente