Introducción a C#
Por Nacho Cabanes, versión 0.93 de 16-abr-2010

5.5. Variables locales y variables globales

Hasta ahora, hemos declarado las variables dentro de "Main". Ahora nuestros programas tienen varios "bloques", así que se comportarán de forma distinta según donde declaremos las variables.

Las variables se pueden declarar dentro de un bloque (una función), y entonces sólo ese bloque las conocerá, no se podrán usar desde ningún otro bloque del programa. Es lo que llamaremos "variables locales".

Por el contrario, si declaramos una variable al comienzo del programa, fuera de todos los "bloques" de programa, será una "variable global", a la que se podrá acceder desde cualquier parte.

Vamos a verlo con un ejemplo. Crearemos una función que calcule la potencia de un número entero (un número elevado a otro), y el cuerpo del programa que la use.

La forma de conseguir elevar un número a otro será a base de multiplicaciones, es decir:

   3 elevado a 5   =   3 • 3 • 3 • 3 • 3

(multiplicamos 5 veces el 3 por sí mismo). En general, como nos pueden pedir cosas como "6 elevado a 100" (o en general números que pueden ser grandes), usaremos la orden "for" para multiplicar tantas veces como haga falta:

/*---------------------------*/
/*  Ejemplo en C# nº 50:     */
/*  ejemplo50.cs             */
/*                           */
/*  Ejemplo de función con   */
/*  variables locales        */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
 
public class Ejemplo50
{
 
  public static int potencia(int nBase, int nExponente) 
  {
    int temporal = 1;        /* Valor que voy hallando */
    int i;                   /* Para bucles */
 
    for(i=1; i<=nExponente; i++)    /* Multiplico "n" veces */
      temporal *= nBase;            /* Y calculo el valor temporal */
 
    return temporal;                /* Tras las multiplicaciones, */
  }                                 /*    obtengo el valor que buscaba */
 
 
  public static void Main()
  {
    int num1, num2;
 
    Console.WriteLine("Introduzca la base: ");
    num1 = Convert.ToInt32( Console.ReadLine() );
 
    Console.WriteLine("Introduzca el exponente: ");
    num2 = Convert.ToInt32( Console.ReadLine() );
 
    Console.WriteLine("{0} elevado a {1} vale {2}", 
        num1, num2, potencia(num1,num2));
  }
 
}
 

En este caso, las variables "temporal" e "i" son locales a la función "potencia": para "Main" no existen. Si en "Main" intentáramos hacer i=5; obtendríamos un mensaje de error.

De igual modo, "num1" y "num2" son locales para "main": desde la función "potencia" no podemos acceder a su valor (ni para leerlo ni para modificarlo), sólo desde "main".

En general, deberemos intentar que la mayor cantidad de variables posible sean locales (lo ideal sería que todas lo fueran). Así hacemos que cada parte del programa trabaje con sus propios datos, y ayudamos a evitar que un error en un trozo de programa pueda afectar al resto. La forma correcta de pasar datos entre distintos trozos de programa es usando los parámetros de cada función, como en el anterior ejemplo.

Ejercicios propuestos:

5.6. Los conflictos de nombres en las variables

¿Qué ocurre si damos el mismo nombre a dos variables locales? Vamos a comprobarlo con un ejemplo:

/*---------------------------*/
/*  Ejemplo en C# nº 51:     */
/*  ejemplo51.cs             */
/*                           */
/*  Dos variables locales    */
/*  con el mismo nombre      */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
 
public class Ejemplo51
{
 
  public static void cambiaN() {
    int n = 7;
    n ++;
  }
 
 
  public static void Main()
  {
    int n = 5;
    Console.WriteLine("n vale {0}", n);
    cambiaN();
    Console.WriteLine("Ahora n vale {0}", n);
  }
 
}
 

El resultado de este programa es:

n vale 5
Ahora n vale 5

¿Por qué? Sencillo: tenemos una variable local dentro de "duplica" y otra dentro de "main". El hecho de que las dos tengan el mismo nombre no afecta al funcionamiento del programa, siguen siendo distintas.

Si la variable es "global", declarada fuera de estas funciones, sí será accesible por todas ellas:

/*---------------------------*/
/*  Ejemplo en C# nº 52:     */
/*  ejemplo52.cs             */
/*                           */
/*  Una variable global      */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
 
public class Ejemplo52
{
 
  static int n = 7;
 
  public static void cambiaN() {
    n ++;
  }
 
 
  public static void Main()
  {
    Console.WriteLine("n vale {0}", n);
    cambiaN();
    Console.WriteLine("Ahora n vale {0}", n);
  }
 
}
 

Dentro de poco, hablaremos de por qué cada uno de los bloques de nuestro programa, e incluso las "variables globales", tienen delante la palabra "static". Será cuando tratemos la "Programación Orientada a Objetos", en el próximo tema.

5.7. Modificando parámetros

Podemos modificar el valor de un dato que recibamos como parámetro, pero posiblemente el resultado no será el que esperamos. Vamos a verlo con un ejemplo:

/*---------------------------*/
/*  Ejemplo en C# nº 53:     */
/*  ejemplo53.cs             */
/*                           */
/*  Modificar una variable   */
/*  recibida como parámetro  */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
 
public class Ejemplo53
{
 
  public static void duplica(int x) {
    Console.WriteLine("  El valor recibido vale {0}", x);
    x = x * 2;
    Console.WriteLine("  y ahora vale {0}", x);
  }
 
  public static void Main()
  {
    int n = 5;
    Console.WriteLine("n vale {0}", n);
    duplica(n);
    Console.WriteLine("Ahora n vale {0}", n);
  }
 
}
 

El resultado de este programa será:

n vale 5
  El valor recibido vale 5
  y ahora vale 10
Ahora n vale 5

Vemos que al salir de la función, los cambios que hagamos a esa variable que se ha recibido como parámetro no se conservan.

Esto se debe a que, si no indicamos otra cosa, los parámetros "se pasan por valor", es decir, la función no recibe los datos originales, sino una copia de ellos. Si modificamos algo, estamos cambiando una copia de los datos originales, no dichos datos.

Si queremos que los cambios se conserven, basta con hacer un pequeño cambio: indicar que la variable se va a pasar "por referencia", lo que se indica usando la palabra "ref", tanto en la declaración de la función como en la llamada, así:

/*---------------------------*/
/*  Ejemplo en C# nº 54:     */
/*  ejemplo54.cs             */
/*                           */
/*  Modificar una variable   */
/*  recibida como parámetro  */
/*                           */
/*  Introduccion a C#,       */
/*    Nacho Cabanes          */
/*---------------------------*/
 
using System;
 
public class Ejemplo54
{
 
  public static void duplica(ref int x) {
    Console.WriteLine("  El valor recibido vale {0}", x);
    x = x * 2;
    Console.WriteLine("  y ahora vale {0}", x);
  }
 
  public static void Main()
  {
    int n = 5;
    Console.WriteLine("n vale {0}", n);
    duplica(ref n);
    Console.WriteLine("Ahora n vale {0}", n);
  }
 
}
 

En este caso sí se modifica la variable n:

n vale 5
  El valor recibido vale 5
  y ahora vale 10
Ahora n vale 10

El hecho de poder modificar valores que se reciban como parámetros abre una posibilidad que no se podría conseguir de otra forma: con "return" sólo se puede devolver un valor de una función, pero con parámetros pasados por referencia podríamos devolver más de un dato. Por ejemplo, podríamos crear una función que intercambiara los valores de dos variables:

public static void intercambia(ref int x, ref int y)

La posibilidad de pasar parámetros por valor y por referencia existe en la mayoría de lenguajes de programación. En el caso de C# existe alguna posibilidad adicional que no existe en otros lenguajes, como los "parámetros de salida". Las veremos más adelante.

Ejercicios propuestos: