2008-10-06 23 views
36

Aparentemente hay mucha variedad de opiniones, desde "Nunca! Siempre encapsule (incluso si es con un simple macro!)" a "No es gran cosa: úselos cuando sea más conveniente que no "¿Cuándo está bien usar una variable global en C?

So.

, motivos concretos específicos (preferiblemente con un ejemplo)

  • Por qué variables globales son peligrosos
  • Cuando las variables globales deben pueden utilizar en lugar de alternativas
  • ¿Qué alternativas existen para aquellos que son tentado de usar variables globales inapropiadamente

Si bien esto es subjetivo, escogeré una respuesta er (que para mí representa mejor la relación amor/odio que todo desarrollador debería tener con los globales) y la comunidad votará la suya justo debajo.

Creo que es importante que los novatos tengan este tipo de referencia, pero no la atempere si existe otra respuesta que sea sustancialmente similar a la suya: agregue un comentario o edite la respuesta de otra persona.

-Adán

Respuesta

39

Las variables siempre deben tener el alcance más pequeño posible. El argumento detrás de eso es que cada vez que aumenta el alcance, tiene más código que potencialmente modifica la variable, por lo tanto, se induce más complejidad en la solución.

Está claro que es preferible evitar el uso de variables globales si el diseño y la implementación lo permiten. Debido a esto, prefiero no usar variables globales a menos que sean realmente necesarias.

No puedo estar de acuerdo con la declaración "never" tampoco, al igual que cualquier otro concepto, las variables globales son una herramienta que se debe usar cuando sea necesario. Prefiero usar variables globales que usar algunas construcciones artificiales (como pasar punteros) que solo enmascararían la intención real. Buenos ejemplos en los que se usan variables globales son las implementaciones de patrones únicos o el acceso de registro en sistemas integrados.

Sobre cómo realmente detectar usos excesivos de variables globales: inspección, inspección, inspección. Cada vez que veo una variable global, me tengo que preguntar a mí mismo: ¿Eso es REALMENTE necesario en un ámbito global?

+0

Además, en el código de producción, es muy frustrante cuando su proyecto tiene que ser reentrante y/o seguro para subprocesos. Si alguien hereda su proyecto de 20,000 líneas en el futuro, imagínese lo divertido que será refacturarlo todo hasta el punto en que sea más eficiente volver a escribirlo desde cero. – DevNull

7

Las variables globales en C son útiles para hacer el código más legible si una variable es requerido por varios métodos (en lugar de pasar la variable en cada método). Sin embargo, son peligrosos porque todas las ubicaciones tienen la capacidad de modificar esa variable, lo que hace que sea potencialmente difícil rastrear errores. Si debe usar una variable global, asegúrese siempre de que solo se modifique directamente mediante un método y haga que todos los que llaman usen ese método. Esto facilitará la depuración de problemas relacionados con los cambios en esa variable.

0

Estoy en el campamento "nunca" aquí; si necesita una variable global, al menos use un singleton pattern. De esta forma, obtendrá los beneficios de la creación de instancias perezosas y no saturará el espacio de nombres global.

+2

Aunque no estoy en desacuerdo, tenga en cuenta que el enfoque aquí es C - no hay clases. ¿Puedes mostrar cómo implementar un singleton en C? Una de las principales ventajas de la programación de OO sobre C es este tipo de patrón de diseño ... –

+0

Los singletons no son la respuesta a las variables globales, solo permiten a los programadores cambiar el estilo de la declaración mientras son flojos (de mala manera) con gestión de por vida Los singletons me causan más problemas que las variables globales. – Torlack

+0

@ Adam Davis: ¿Una función que devuelve una instancia que está almacenada en una variable global con alcance de archivo? Aunque no estoy seguro de que me guste esto mejor que una simple variable global. – aib

1

Es una herramienta como cualquier otra usualmente usada en exceso, pero no creo que sean malas.

Por ejemplo, tengo un programa que realmente funciona como una base de datos en línea. Los datos se almacenan en la memoria, pero otros programas pueden manipularlo. Existen rutinas internas que actúan de forma muy parecida a los procedimientos almacenados y desencadenantes en una base de datos.

Este programa tiene cientos de variables globales, pero si lo piensas, ¿qué es una base de datos sino un gran número de variables globales?

Este programa ha estado en uso durante aproximadamente diez años a través de muchas versiones y nunca ha sido un problema y lo volvería a hacer en un minuto.

Debo admitir que en este caso los vars globales son objetos que tienen métodos utilizados para cambiar el estado del objeto. Así que rastrear quién cambió el objeto durante la depuración no es un problema ya que siempre puedo establecer un punto de interrupción en la rutina que cambia el estado del objeto. O incluso más simple, simplemente enciendo el registro integrado que registra los cambios.

2

Debe tener en cuenta en qué contexto también se utilizará la variable global. En el futuro, querrás que este código se duplique.

Por ejemplo, si está utilizando un zócalo dentro del sistema para acceder a un recurso. En el futuro, si desea acceder a más de uno de estos recursos, si la respuesta es sí, me mantendría alejado de los globales en primer lugar, por lo que no se requerirá un refactor importante.

4

Vengo del campo "nunca", hasta que comencé a trabajar en la industria de la defensa. Existen algunos estándares de la industria que requieren que el software use variables globales en lugar de memoria dinámica (malloc en el caso C). Tengo que repensar mi enfoque de la asignación de memoria dinámica para algunos de los proyectos en los que trabajo. Si puede proteger la memoria "global" con los semáforos, hilos, etc. apropiados, esto puede ser un enfoque aceptable para la gestión de su memoria.

+3

Interesante. La industria de defensa me puso firmemente en el campo de "nunca". –

+1

Trabajé en un sistema integrado donde la asignación de memoria dinámica no estaba disponible. En ese caso, necesitábamos variables 'globales' asignadas en el espacio RAM (para datos de imagen) por el vinculador y teníamos que controlar el acceso entre múltiples hilos. Es una escuela de pensamiento diferente, pero puede funcionar. –

1

Las variables globales se deben usar cuando varias funciones necesitan acceder a los datos o escribir en un objeto. Por ejemplo, si debe pasar datos o una referencia a varias funciones, como un único archivo de registro, un grupo de conexiones o una referencia de hardware a la que se debe acceder a través de la aplicación. Esto evita declaraciones de funciones muy largas y grandes asignaciones de datos duplicados.

Normalmente, no use utilice variables globales a menos que sea absolutamente necesario porque las variables globales solo se limpian cuando se le indica explícitamente que lo haga o su programa finaliza. Si está ejecutando una aplicación multiproceso, varias funciones pueden escribir en la variable al mismo tiempo. Si tiene un error, rastrear ese error puede ser más difícil porque no sabe qué función está cambiando la variable. También se encuentra con el problema de nombrar conflictos a menos que use una convención de nomenclatura que explícitamente le dé a las variables globales un nombre único.

5

Cuando no está preocupado por el código seguro para subprocesos: utilícelos donde sea que tenga sentido, en otras palabras, donde tenga sentido expresar algo como un estado global.

Cuando su código puede ser de subprocesos múltiples: evite a toda costa. Resuma variables globales en colas de trabajo o alguna otra estructura segura para subprocesos, o si es absolutamente necesario, envuélvalas en bloqueos, teniendo en cuenta que estos son probablemente cuellos de botella en el programa.

10

Considere este koan: "si el alcance es lo suficientemente estrecho, todo es global".

Todavía es muy posible en esta edad tener que escribir un programa de utilidad muy rápido para hacer un trabajo de una sola vez.

En tales casos, la energía requerida para crear un acceso seguro a las variables es mayor que la energía ahorrada por los problemas de depuración en una utilidad tan pequeña.

Este es el único caso que puedo pensar de improviso donde las variables globales son sabias, y es relativamente raro. Los programas útiles y novedosos, tan pequeños que pueden mantenerse completamente dentro de la memoria a corto plazo del cerebro, son cada vez más infrecuentes, pero aún existen.

De hecho, podría afirmar audazmente que si el programa no es tan pequeño, entonces las variables globales deberían ser ilegales.

  • Si la variable nunca cambiará, entonces es una constante, no una variable.
  • Si la variable requiere acceso universal, deben existir dos subrutinas para obtenerla y configurarla, y deben estar sincronizadas.
  • Si el programa comienza pequeño, y podría ser más grande más tarde, entonces codifique como si el programa fuera grande hoy, y elimine las variables globales. ¡No todos los programas crecerán! (Aunque, por supuesto, eso supone que el programador está dispuesto a descartar el código a veces.)
15

La única manera en que puede hacer que las variables globales funcionen es darles nombres que aseguren que son únicas.

Ese nombre suele tener un prefijo asociado a algún "módulo" o colección de funciones para las cuales la variable global está particularmente enfocada o es significativa.

Esto significa que la variable "pertenece" a esas funciones, es parte de ellas. De hecho, lo global generalmente se puede "envolver" con una pequeña función que acompaña a las otras funciones, en el mismo prefijo de nombre de archivo .h.

Bonus.

Cuando lo haces, de repente, ya no es realmente global. Ahora es parte de algún módulo de funciones relacionadas.

Esto puede siempre hacerse. Con un poco de pensamiento, todas las variables anteriormente globales se pueden asignar a una colección de funciones, asignadas a un archivo específico .h y aisladas con funciones que le permiten cambiar la variable sin romper nada.

En lugar de decir "nunca usar variables globales", puede decir "asigne las responsabilidades de la variable global a algún módulo donde tenga más sentido".

+0

+1 - Bravo. Estos son los tipos de cosas que la gente hacía en los viejos tiempos antes de saber que las variables globales son tan "malas". Sorprendentemente, esos viejos programas todavía funcionaban y a menudo aún podían mantenerse. :) – Torlack

0

Las constantes globales son útiles: obtienes más seguridad de tipo que las macros de preprocesador y aún así es tan fácil cambiar el valor si decides que lo necesitas.

Las variables globales tienen algunos usos, por ejemplo, si la operación de muchas partes de un programa depende de un estado particular en la máquina de estados. Siempre que limite el número de lugares que pueden MODIFICAR la variable que rastrea los errores que lo involucran no es tan malo.

Las variables globales se vuelven peligrosas casi tan pronto como se crea más de un hilo.En ese caso, debería limitar el alcance a (como máximo) un archivo global (al declararlo estático) variable y métodos getter/setter que lo protegen del acceso múltiple donde podría ser peligroso.

3

La complejidad del código no es la única optimización de preocupación. Para muchas aplicaciones, la optimización del rendimiento tiene una prioridad mucho mayor. Pero, lo que es más importante, el uso de variables globales puede REDUCIR drásticamente la complejidad del código en muchas situaciones. Hay muchas situaciones, tal vez especializadas, en las que las variables globales no son solo una solución aceptable, sino preferida. Mi ejemplo especializado favorito es su uso para proporcionar comunicación entre el hilo principal de una aplicación con una función de devolución de llamada de audio que se ejecuta en un hilo en tiempo real.

Es engañoso sugerir que las variables globales son una responsabilidad en las aplicaciones de subprocesos múltiples, ya que CUALQUIER variable, independientemente del alcance, es una responsabilidad potencial si está expuesta a cambios en más de un hilo.

Utilice las variables globales con moderación. Las estructuras de datos se deben usar siempre que sea posible para organizar y aislar el uso del espacio de nombres global.

El alcance variable ofrece a los programadores una protección muy útil, pero puede tener un costo. Esta noche escribí sobre variables globales porque soy un experimentado programador de Objective-C que a menudo se frustra con las barreras que los lugares de orientación al objeto tienen sobre el acceso a los datos. Yo argumentaría que el fanatismo antiglobal proviene principalmente de programadores más jóvenes y teóricos experimentados principalmente con API orientadas a objetos aisladas sin una experiencia profunda y práctica de las API de nivel de sistema y su interacción en el desarrollo de aplicaciones. Pero tengo que admitir que me siento frustrado cuando los vendedores usan el espacio de nombres de manera descuidada. Varias distribuciones de Linux tenían "PI" y "TWOPI" predefinidas globalmente, por ejemplo, lo que rompió gran parte de mi código personal.

1

puedo pensar en varias razones: los propósitos

depuración/pruebas (de advertencia - no han probado este código):

#include <stdio.h> 
#define MAX_INPUT 46 
int runs=0; 
int fib1(int n){ 
    ++runs; 
    return n>2?fib1(n-1)+fib1(n-2):1; 
}; 
int fib2(int n,int *cache,int *len){ 
    ++runs; 
    if(n<=2){ 
     if(*len==2) 
      return 1; 
     *len=2; 
     return cache[0]=cache[1]=1; 
    }else if(*len>=n) 
     return cache[n-1]; 
    else{ 
     if(*len!=n-1) 
      fib2(n-1,cache,len); 
     *len=n; 
     return cache[n-1]=cache[n-2]+cache[n-3]; 
    }; 
}; 
int main(){ 
    int n; 
    int cache[MAX_INPUT]; 
    int len=0; 
    scanf("%i",&n); 
    if(!n||n>MAX_INPUT) 
     return 0; 
    printf("fib1(%i)==%i",n,fib1(n)); 
    printf(", %i run(s)\n",runs); 
    runs=0; 
    printf("fib2(%i)==%i",n,fib2(n,&cache,&len)); 
    printf(", %i run(s)\n",runs); 
    main(); 
}; 

utilicé las variables con ámbito de fib2, pero eso es un escenario más donde los globales pueden ser útiles (funciones matemáticas puras que necesitan almacenar datos para evitar tomar para siempre).

programas usados ​​sólo una vez (por ejemplo, para un concurso), o cuando se necesita el tiempo de desarrollo que acortarse

globales son útiles como constantes escritos, en los que una función requiere algún lugar * int en lugar de int.

Generalmente evito los globales si tengo la intención de utilizar el programa por más de un día.

1
  • cuándo no usar: Las variables globales son peligrosos debido a que la única manera de saber nunca cómo la variable global cambiado es trazar todo el código fuente dentro del archivo .c dentro del cual se declaran (o, todo .c archivos si es externo también). Si su código presenta errores, debe buscar su (s) archivo (s) fuente (s) completo (s) para ver qué funciones lo cambian y cuándo. Es una pesadilla para depurar cuando sale mal. A menudo damos por sentado el ingenio detrás del concepto de variables locales con gracia que salían del alcance - es fácil de rastrear
  • Cuándo utilizar: Las variables globales se deben usar cuando su utilización no se enmascara en exceso y donde el costo El uso de variables locales es excesivamente complejo hasta el punto de comprometer la legibilidad.Con esto, me refiero a la necesidad de tener que agregar un parámetro adicional a los argumentos de la función y retorna y pasa punteros, entre otras cosas. Tres ejemplos clásicos: cuando uso pop y push stack, esto se comparte entre funciones. Por supuesto, podría usar variables locales, pero luego tendría que pasar punteros como un parámetro adicional. El segundo ejemplo clásico se puede encontrar en K & R "The C Programming Language" donde definen un getch() y ungetch() funciones que comparten una matriz de búfer de caracteres global. Una vez más, no es necesario que sea global, pero ¿vale la pena la complejidad añadida cuando es bastante difícil estropear el uso del buffer? El tercer ejemplo es algo que encontrarás en el espacio incrustado entre los aficionados a Arduino. Una gran cantidad de funciones dentro de la función del bucle todos comparten la función millis() que es el momento instantáneo de cuando se invoca la función. Debido a que la velocidad del reloj no es infinita, millis() será que difieren en un solo bucle. Para hacerlo constante, tome una instantánea del tiempo anterior a cada ciclo y guárdelo en una variable global. La instantánea de tiempo ahora será la misma que cuando se accede por las muchas funciones.
  • Alternativas: No mucho. Limítese al alcance local tanto como sea posible, especialmente al comienzo del proyecto, y no al revés. A medida que crezca el proyecto y considere que la complejidad se puede reducir utilizando variables globales, hágalo, pero solo si cumple con los requisitos del punto dos. Y recuerde, utilizar el alcance local y tener un código más complicado es el mal menor en comparación con el uso irresponsable de variables globales.
Cuestiones relacionadas