En C / C ++, ¿las variables globales son tan malas como mi profesor cree que son?
En C / C ++, ¿las variables globales son tan malas como mi profesor cree que son?
Respuestas:
El problema con las variables globales es que, dado que cada función tiene acceso a ellas, se hace cada vez más difícil determinar qué funciones realmente leen y escriben estas variables.
Para comprender cómo funciona la aplicación, debe tener en cuenta cada función que modifica el estado global. Eso se puede hacer, pero a medida que la aplicación crezca se volverá más difícil hasta el punto de ser prácticamente imposible (o al menos una completa pérdida de tiempo).
Si no confía en variables globales, puede pasar el estado entre diferentes funciones según sea necesario. De esa manera, tendrá una mejor oportunidad de comprender lo que hace cada función, ya que no necesita tener en cuenta el estado global.
Lo importante es recordar el objetivo general: claridad
La regla "no hay variables globales" está ahí porque la mayoría de las veces, las variables globales hacen que el significado del código sea menos claro.
Sin embargo, como muchas reglas, las personas recuerdan la regla y no lo que la regla pretendía hacer.
He visto programas que parecen duplicar el tamaño del código al pasar una enorme cantidad de parámetros simplemente para evitar el mal de las variables globales. Al final, el uso de globals habría hecho que el programa fuera más claro para quienes lo leen. Al adherirse sin pensar a la palabra de la regla, el programador original había fallado la intención de la regla.
Entonces, sí, los globales son a menudo malos. Pero si siente que al final, la intención del programador se aclara con el uso de variables globales, entonces continúe. Sin embargo, recuerde la caída en la claridad que se produce automáticamente cuando obliga a alguien a acceder a una segunda pieza de código (los globales) para comprender cómo funciona la primera pieza.
Mi profesor solía decir algo como: usar variables globales está bien si las usas correctamente. No creo que alguna vez haya sido bueno usándolos correctamente, así que rara vez los usé en absoluto.
static
muchas variables globales, el lenguaje es C. Al estar confinados a unidades de traducción relativamente pequeñas, comienzan a parecerse a las variables de clase de los objetos C ++.
program lifetime, file scope variables
. Y se vuelven bastante globales una vez que pasa un puntero a la variable al mundo exterior (lo cual es imposible con las variables automáticas) ..
static
variables globales tienen un alcance limitado para la misma unidad de traducción. Pero tienen una vida útil hasta el final del programa como cualquier variable global.
Las variables globales solo deben usarse cuando no tienes alternativa. Y sí, eso incluye Singletons. El 90% del tiempo, se introducen variables globales para ahorrar el costo de pasar un parámetro. Y luego ocurre la codificación de subprocesos múltiples / pruebas unitarias / mantenimiento, y tiene un problema.
Entonces sí, en el 90% de las situaciones las variables globales son malas. Es probable que no veas las excepciones en tus años universitarios. Una excepción que puedo pensar fuera de mi cabeza es tratar con objetos inherentemente globales, como tablas de interrupción. Cosas como la conexión DB parece ser global, pero no lo es.
El problema que crean las variables globales para el programador es que expande la superficie de acoplamiento entre componentes entre los diversos componentes que utilizan las variables globales. Lo que esto significa es que a medida que aumenta el número de componentes que utilizan una variable global, la complejidad de las interacciones también puede aumentar. Este aumento del acoplamiento generalmente hace que los defectos sean más fáciles de inyectar en el sistema cuando se realizan cambios y también hace que los defectos sean más difíciles de diagnosticar y corregir. Este aumento de acoplamiento también puede reducir el número de opciones disponibles al hacer cambios y puede aumentar el esfuerzo requerido para los cambios, ya que a menudo se debe rastrear a través de los diversos módulos que también usan la variable global para determinar las consecuencias de los cambios.
El propósito de la encapsulación , que es básicamente lo opuesto al uso de variables globales, es disminuir el acoplamiento para hacer que la comprensión y el cambio de la fuente sean más fáciles y seguros y se prueben más fácilmente. Es mucho más fácil usar pruebas unitarias cuando no se usan variables globales.
Por ejemplo, si tiene una variable entera global simple que se está utilizando como un indicador enumerado que varios componentes usan como máquina de estado y luego realiza un cambio agregando un nuevo estado para un nuevo componente, debe rastrear todos los demás componentes para garantizar que el cambio no los afecte. Un ejemplo de un posible problema sería si una switch
declaración para probar el valor de la variable global de enumeración con case
declaraciones para cada uno de los valores actuales se está utilizando en varios lugares y sucede que algunas de las switch
declaraciones no tienen un default
caso para manejar un valor inesperado para el global de repente tiene un comportamiento indefinido en lo que respecta a la aplicación.
Por otro lado, el uso de un área de datos compartidos podría usarse para contener un conjunto de parámetros globales a los que se hace referencia en toda la aplicación. Este enfoque a menudo se usa con aplicaciones integradas con pequeñas huellas de memoria.
Cuando se usan variables globales en este tipo de aplicaciones, normalmente la responsabilidad de escribir en el área de datos se asigna a un solo componente y todos los demás componentes ven el área como const
y leen, nunca escriben en ella. Tomar este enfoque limita los problemas que pueden desarrollarse.
Algunos problemas de las variables globales que deben solucionarse
Cuando se modifica el origen de una variable global, como una estructura, todo lo que lo use debe volver a compilarse para que todo lo que use la variable conozca su tamaño real y la plantilla de memoria.
Si más de un componente puede modificar la variable global, puede encontrarse con problemas con datos inconsistentes en la variable global. Con una aplicación de subprocesos múltiples, probablemente necesitará agregar algún tipo de bloqueo o región crítica para proporcionar una manera de que solo un subproceso a la vez pueda modificar la variable global y cuando un subproceso está modificando la variable, todos los cambios se completan y confirmado antes de que otros hilos puedan consultar la variable o modificarla.
La depuración de una aplicación multiproceso que utiliza una variable global puede ser más difícil. Puede encontrarse con condiciones de carrera que pueden crear defectos que son difíciles de replicar. Con varios componentes que se comunican a través de una variable global, especialmente en una aplicación de subprocesos múltiples, puede ser muy difícil entender qué componente está cambiando la variable y cuándo está cambiando la variable.
El choque de nombres puede ser un problema con el uso de variables globales. Una variable local que tiene el mismo nombre que una variable global puede ocultar la variable global. También se encuentra con el problema de la convención de nomenclatura cuando usa el lenguaje de programación C. Una solución es dividir el sistema en subsistemas con las variables globales para un subsistema particular, todo comenzando con las mismas primeras tres letras (vea esto para resolver colisiones de espacios de nombres en el objetivo C ). C ++ proporciona espacios de nombres y con C puede solucionar esto creando una estructura visible globalmente cuyos miembros son varios elementos de datos y punteros a datos y funciones que se proporcionan en un archivo como estáticos, por lo tanto, con visibilidad de archivo solo para que solo puedan ser referenciados a través de ellos. La estructura globalmente visible.
En algunos casos, la intención de la aplicación original se cambia para que las variables globales que proporcionan el estado de un solo subproceso se modifiquen para permitir que se ejecuten varios subprocesos duplicados. Un ejemplo sería una aplicación simple diseñada para un solo usuario que utiliza variables globales para el estado y luego una solicitud de la administración para agregar una interfaz REST para permitir que las aplicaciones remotas actúen como usuarios virtuales. Entonces, ahora tiene que duplicar las variables globales y su información de estado para que el usuario individual y cada uno de los usuarios virtuales de las aplicaciones remotas tengan su propio conjunto único de variables globales.
Usando C ++ namespace
y la struct
técnica para C
Para el lenguaje de programación C ++, la namespace
directiva es de gran ayuda para reducir las posibilidades de un choque de nombres. namespace
junto con class
y las diversas palabras clave de acceso ( private
, protected
y public
) proporcionan la mayoría de las herramientas que necesita para encapsular variables. Sin embargo, el lenguaje de programación C no proporciona esta directiva. Esta publicación de stackoverflow, espacios de nombres en C , proporciona algunas técnicas para C.
Una técnica útil es tener un área de datos residente de memoria única que se define como una struct
que tiene visibilidad global y dentro de esto struct
hay punteros a las diversas variables y funciones globales que están siendo expuestas. Las definiciones reales de las variables globales tienen un alcance de archivo usando la static
palabra clave. Si luego usa la const
palabra clave para indicar cuáles son de solo lectura, el compilador puede ayudarlo a imponer el acceso de solo lectura.
El uso de la struct
técnica también puede encapsular lo global para que se convierta en un tipo de paquete o componente que sea global. Al tener un componente de este tipo, resulta más fácil gestionar los cambios que afectan lo global y la funcionalidad que utiliza el global.
Sin embargo, aunque namespace
la struct
técnica puede ayudar a gestionar los conflictos de nombres, los problemas subyacentes del acoplamiento entre componentes que el uso de los globales introduce especialmente en una aplicación moderna de múltiples subprocesos, todavía existen.
Sí, pero no incurrirá en el costo de las variables globales hasta que deje de trabajar en el código que usa variables globales y comience a escribir algo más que use el código que usa variables globales. Pero el costo sigue ahí.
En otras palabras, es un costo indirecto a largo plazo y, como tal, la mayoría de la gente piensa que no está mal.
Si es posible, su código terminará bajo una revisión intensiva durante un juicio en la Corte Suprema , entonces debe asegurarse de evitar las variables globales.
Ver este artículo: el código del alcoholímetro con errores refleja la importancia de la revisión de la fuente
Hubo algunos problemas con el estilo del código que fueron identificados por ambos estudios. Una de las cuestiones estilísticas que preocuparon a los revisores fue el uso extensivo de variables globales desprotegidas . Esto se considera deficiente porque aumenta el riesgo de que el estado del programa se vuelva inconsistente o que los valores se modifiquen o sobrescriban inadvertidamente. Los investigadores también expresaron cierta preocupación por el hecho de que la precisión decimal no se mantiene de manera consistente en todo el código.
Hombre, ¡apuesto a que esos desarrolladores desearían no haber usado variables globales!
Las variables globales son tan malas como las haces, nada menos.
Si está creando un programa completamente encapsulado, puede usar globales. Es un "pecado" usar globales, pero programar los pecados es muy filosófico.
Si revisa L.in.oleum , verá un lenguaje cuyas variables son únicamente globales. No es escalable porque todas las bibliotecas no tienen más remedio que usar globales.
Dicho esto, si tiene opciones y puede ignorar la filosofía del programador, los globales no son tan malos.
Tampoco Gotos, si los usas bien.
El gran problema "malo" es que, si los usas mal, la gente grita, el módulo de aterrizaje de Marte se estrella y el mundo explota ... o algo así.
Respondería a esta pregunta con otra pregunta: ¿ Usas singeltons / Are singeltons bad?
Porque (casi todo) el uso de singelton es una variable global glorificada.
El problema es menos que son malos , y más que son peligrosos . Tienen su propio conjunto de ventajas y desventajas, y hay situaciones en las que son la forma más eficiente o la única para lograr una tarea en particular. Sin embargo, son muy fáciles de usar mal, incluso si toma medidas para usarlos siempre correctamente.
Algunas ventajas:
Algunas desventajas:
Tenga en cuenta, si lo desea, que los dos primeros pros y los dos primeros contras que enumeré son exactamente lo mismo, pero con una redacción diferente. Esto se debe a que las características de una variable global pueden ser útiles, pero las mismas características que las hacen útiles son la fuente de todos sus problemas.
Algunas posibles soluciones a algunos de los problemas:
Globals
o GlobalVars
), o use una convención de nomenclatura estandarizada para variables globales (como global_[name]
o g_module_varNameStyle
(como se menciona por subrayado_d en los comentarios) )). Esto documentará su uso (puede encontrar código que usa variables globales buscando el espacio de nombres / nombre de estructura) y minimizará el impacto en el espacio de nombres global.extern
en el encabezado asociado, de modo que su uso se pueda limitar a las unidades de compilación que necesitan acceder a ellos. Si su código se basa en muchas variables globales, pero cada unidad de compilación solo necesita acceder a un puñado de ellas, podría considerar ordenarlas en múltiples archivos fuente, por lo que es más fácil limitar el acceso de cada archivo a variables globales.Si son buenos o malos depende de cómo los uses. La mayoría tiende a usarlos mal, de ahí la cautela general hacia ellos. Si se usan adecuadamente, pueden ser una gran ayuda; si se usa mal, sin embargo, pueden y van a volver a morder cuándo y con qué menos te lo esperas.
Una buena manera de verlo es que ellos mismos no son malos, pero permiten un mal diseño y pueden multiplicar exponencialmente los efectos del mal diseño.
Incluso si no tiene la intención de usarlos, es mejor saber cómo usarlos de manera segura y elegir no hacerlo, que no usarlos porque no sabe cómo usarlos de manera segura. Si alguna vez se encuentra en una situación en la que necesita mantener un código preexistente que se basa en variables globales, es posible que tenga dificultades si no sabe cómo usarlas correctamente.
g_module_varNameStyle
perfectamente legible. Para ser claros, no estoy usando globals si puedo evitarlo fácilmente, palabra clave fácilmente , porque desde que dejé de creer que deben evitarse, o más bien ofuscado , a toda costa, lo estoy pasando mucho mejor, y mi el código es (¡shock!) mucho más ordenado
Como alguien dijo (estoy parafraseando) en otro hilo "Reglas como esta no deberían romperse, hasta que comprenda completamente las consecuencias de hacerlo".
Hay momentos en que las variables globales son necesarias, o al menos muy útiles (por ejemplo, trabajar con devoluciones de llamada definidas por el sistema). Por otro lado, también son muy peligrosos por todas las razones que le han dicho.
Hay muchos aspectos de la programación que probablemente deberían dejarse a los expertos. A veces necesitas un cuchillo muy afilado. Pero no puedes usar uno hasta que estés listo ...
Las variables globales son generalmente malas, especialmente si otras personas están trabajando en el mismo código y no quieren pasar 20 minutos buscando todos los lugares a los que se hace referencia a la variable. Y agregar hilos que modifican las variables trae un nuevo nivel de dolores de cabeza.
Las constantes globales en un espacio de nombres anónimo utilizado en una sola unidad de traducción son buenas y ubicuas en aplicaciones y bibliotecas profesionales. Pero si los datos son mutables, y / o tienen que ser compartidos entre múltiples TU, es posible que desee encapsularlos, si no es por el bien del diseño, por el bien de cualquiera que depure o trabaje con su código.
Usar variables globales es como barrer la tierra debajo de una alfombra. Es una solución rápida, y mucho más fácil a corto plazo que obtener un recogedor de polvo o una aspiradora para limpiarlo. Sin embargo, si alguna vez terminas moviendo la alfombra más tarde, tendrás una gran sorpresa debajo.
Creo que tu profesor está tratando de detener un mal hábito incluso antes de que comience.
Las variables globales tienen su lugar y, como muchas personas dicen, saber dónde y cuándo usarlas puede ser complicado. Así que creo que en lugar de entrar en el meollo del por qué, cómo, cuándo y dónde de las variables globales, su profesor decidió prohibirlo. Quién sabe, podría desterrarlos en el futuro.
Absolutamente no. Aunque abusar de ellos ... eso es malo.
Eliminarlos sin pensar por el simple hecho de ser eso ... sin sentido. A menos que conozca las ventajas y desventajas, es mejor mantenerse alejado y hacer lo que le han enseñado / aprendido, pero no hay nada implícitamente incorrecto con las variables globales. Cuando comprenda mejor los pros y los contras, tome su propia decisión.
Las variables globales están bien en programas pequeños, pero horribles si se usan de la misma manera en programas grandes.
Esto significa que puede acostumbrarse fácilmente a usarlos mientras aprende. Esto es de lo que tu profesor está tratando de protegerte.
Cuando tenga más experiencia, será más fácil aprender cuando estén bien.
No, no son malos en absoluto. Es necesario mirar el código (máquina) producido por el compilador para hacer esta determinación, a veces es mucho peor usar un local que uno global. También tenga en cuenta que poner "estático" en una variable local básicamente lo convierte en global (y crea otros problemas feos que un global real resolvería). Los "globales locales" son particularmente malos.
Los globales también le dan un control limpio sobre el uso de su memoria, algo mucho más difícil de hacer con los locales. En la actualidad, eso solo importa en entornos integrados donde la memoria es bastante limitada. Algo que debe saber antes de asumir que embebido es lo mismo que otros entornos y asumir que las reglas de programación son las mismas en todos los ámbitos.
Es bueno que cuestione las reglas que se enseñan, la mayoría de ellas no son por las razones que le están diciendo. Sin embargo, la lección más importante no es que esta es una regla para llevar contigo para siempre, sino que es una regla que se debe cumplir para aprobar esta clase y seguir adelante. En la vida, encontrará que para la compañía XYZ tendrá otras reglas de programación que finalmente tendrá que respetar para seguir recibiendo un cheque de pago. En ambas situaciones puede argumentar la regla, pero creo que tendrá mucha mejor suerte en el trabajo que en la escuela. Eres solo otro de muchos estudiantes, tu asiento será reemplazado pronto, los profesores no lo harán, en un trabajo eres uno de un pequeño equipo de jugadores que tiene que ver este producto hasta el final y en ese entorno las reglas desarrolladas son para beneficio de los miembros del equipo, así como del producto y la empresa, así que si todos tienen una mentalidad similar o si para un producto en particular hay buenas razones de ingeniería para violar algo que aprendiste en la universidad o algún libro sobre programación genérica, entonces vende tu idea al equipo y escríbela como un método válido, si no el preferido. . Todo es un juego justo en el mundo real.
Si sigue todas las reglas de programación que le enseñaron en la escuela o en los libros, su carrera de programación será extremadamente limitada. Es probable que pueda sobrevivir y tener una carrera fructífera, pero la amplitud y el ancho de los entornos disponibles para usted serán extremadamente limitados. Si sabe cómo y por qué la regla está ahí y puede defenderla, eso es bueno, si su razón es "porque mi maestro lo dijo", bueno, eso no es tan bueno.
Tenga en cuenta que temas como este a menudo se discuten en el lugar de trabajo y seguirán siéndolo, a medida que evolucionan los compiladores y procesadores (y los idiomas), así lo hacen este tipo de reglas y sin defender su posición y posiblemente alguien con otra opinión le enseñe una lección seguir adelante
Mientras tanto, simplemente haz lo que diga el que más fuerte o que diga el palo más grande (hasta el momento en que seas el que grite más fuerte y lleve el palo más grande).
Me gustaría argumentar en contra del punto que se hace a lo largo de este hilo que hace que el multihilo sea más difícil o imposible per se. Las variables globales son estados compartidos, pero las alternativas a los globales (por ejemplo, pasar punteros) también pueden compartir el estado. El problema con los subprocesos múltiples es cómo usar correctamente el estado compartido, no si ese estado se comparte a través de una variable global u otra cosa.
La mayoría de las veces cuando haces subprocesos múltiples necesitas compartir algo. En un patrón productor-consumidor, por ejemplo, puede compartir alguna cola segura para subprocesos que contiene las unidades de trabajo. Y puede compartirlo porque esa estructura de datos es segura para subprocesos. Si esa cola es global o no es completamente irrelevante cuando se trata de seguridad de subprocesos.
La esperanza implícita expresada a lo largo de este hilo de que la transformación de un programa de un solo subproceso a varios subprocesos será más fácil cuando no se utilizan globales es ingenuo. Sí, los globales hacen que sea más fácil dispararse en el pie, pero hay muchas maneras de dispararse.
No estoy abogando por los globales, ya que los otros puntos siguen en pie, mi punto es simplemente que el número de hilos en un programa no tiene nada que ver con el alcance variable.
Sí, porque si dejas que los programadores incompetentes los usen (lee el 90%, especialmente los científicos) terminas con más de 600 variables globales distribuidas en más de 20 archivos y un proyecto de 12,000 líneas donde el 80% de las funciones se anulan, devuelven anuladas y operan enteramente en estado global.
Rápidamente se hace imposible entender lo que está sucediendo en cualquier punto a menos que conozca todo el proyecto.
El uso de variables globales en realidad depende de los requisitos. Su ventaja es que reduce la sobrecarga de pasar los valores repetidamente.
Pero su profesor tiene razón porque plantea problemas de seguridad, por lo que debe evitarse el uso de variables globales tanto como sea posible. Las variables globales también crean problemas que a veces son difíciles de depurar .
Por ejemplo:-
Situaciones cuando los valores de las variables se modifican en tiempo de ejecución . En ese momento es difícil identificar qué parte del código lo está modificando y en qué condiciones.
Global son buenos cuando se trata de configuración . Cuando queremos que nuestra configuración / cambios tengan un impacto global en todo el proyecto .
Entonces podemos cambiar una configuración y los cambios se dirigen a todo el proyecto . Pero debo advertirte que tendrías que ser muy inteligente para usar globals.
Tarde o temprano tendrá que cambiar cómo se establece esa variable o qué sucede cuando se accede a ella, o simplemente necesita buscar dónde se cambia.
Prácticamente siempre es mejor no tener variables globales. Simplemente escriba los métodos get y set de la presa, y sea flexible cuando los necesite un día, semana o mes después.
Por lo general, uso valores globales para los valores que rara vez se cambian como singletons o punteros de función a funciones en una biblioteca cargada dinámicamente. El uso de globales mutables en aplicaciones multiproceso tiende a generar errores difíciles de rastrear, por lo que trato de evitar esto como una regla general.
El uso de un argumento global en lugar de pasar un argumento es a menudo más rápido, pero si está escribiendo una aplicación multiproceso, que a menudo hace hoy en día, generalmente no funciona muy bien (puede usar estadísticas de subprocesos, pero la ganancia de rendimiento es cuestionable) .
En las aplicaciones web dentro de una empresa se puede utilizar para mantener datos específicos de sesión / ventana / hilo / usuario en el servidor por razones de optimización y para proteger contra la pérdida de trabajo donde la conexión es inestable. Como se mencionó, las condiciones de carrera deben ser manejadas. Utilizamos una sola instancia de una clase para esta información y se maneja cuidadosamente.
Al final del día, su programa o aplicación aún puede funcionar, pero es cuestión de estar ordenado y tener una comprensión completa de lo que está sucediendo. Si comparte un valor variable entre todas las funciones, puede ser difícil rastrear qué función está cambiando el valor (si la función lo hace) y hace que la depuración sea un millón de veces más difícil
la seguridad es menor significa que cualquiera puede manipular las variables si se declaran globales, para que esto explique, tome este ejemplo si tiene saldo como una variable global en su programa bancario, la función de usuario puede manipular esto, así como el funcionario bancario también puede manipular Esto significa que hay un problema. Solo el usuario debe tener la función de solo lectura y retiro, pero el empleado del banco puede agregar la cantidad cuando el usuario entrega personalmente el efectivo en el escritorio. Esta es la forma en que funciona
En una aplicación multiproceso, use variables locales en lugar de variables globales para evitar una condición de carrera.
Se produce una condición de carrera cuando varios subprocesos acceden a un recurso compartido, con al menos un subproceso que tiene acceso de escritura a los datos. Entonces, el resultado del programa no es predecible, y depende del orden de acceso a los datos por diferentes hilos.
Más sobre esto aquí, https://software.intel.com/en-us/articles/use-intel-parallel-inspector-to-find-race-conditions-in-openmp-based-multithreaded-code