Titulo.jpg (10742 bytes)
FlechaI.jpg (2886 bytes) casa.jpg (31539 bytes) FlechaDA.jpg (3959 bytes)

Tipos de datos propios
arreglos

enum

typedef

struct

punteros


La correcta organizaci�n de la informaci�n que se maneja en un programa es decisivo para el eficiente funcionamiento de este. 
Para ello C++ brinda un grupo de herramientas importantes. As� struct, enum y typedef permiten generar los tipos de datos que se necesiten, tomando como base los propios de C++ u otros previamente definidos.

Arreglos
Es muy frecuente en C++ tener que utilizar variables que contengan una sucesi�n de valores de un mismo tipo de datos. Para ello se utilizan los arreglos.

Los arreglos pueden estar formados por cualquiera de los tipos de datos propios de C++, o de los elaborados por el programador. Se denotan con los signos [] a continuaci�n del nombre de la variable. Las siguientes son variables de tipo arreglo utilizables en C++:

int distancias[10];
float
tiempos[20];
char frase[25];

En estos casos la variable �distancias� tendr� diez valores de tipo int, la variable �tiempos� veinte valores de tipo float y la variable �frase� veinticinco valores de tipo char. La secuencia de valores comenzar� en cero y terminar� un n�mero antes del que indica el valor entre par�ntesis cuadrados, todo lo cual debe ser tomado en cuenta al hacer la asignaci�n de valores a estos tipos de variables.

Es importante el autocontrol sobre el n�mero de elementos que tiene un arreglo, ya que C++ no alerta cuando se ha sobrepasado el rango definido, con lo que podemos entrar en zonas de memoria que est�n usadas por otras variables, o por el propio programa, con lo que crear�amos serios da�os en su ejecuci�n

En C++ los arreglos de char son los que forman las cadenas para situar la informaci�n del tipo de cadenas de caracteres.

Existe tambi�n la posibilidad de declarar datos, variables o constantes, que sean arreglos, pero con m�s de una dimensi�n. Para ello se situar�n dos corchetes, uno a continuaci�n del otro. As� ,tendr�amos que una matriz de 3X4 ser�a:

float matriz[3][4];

Tambi�n se puede tener m�s dimensiones agregando los correspondientes corchetes y valores.

Los enumeradores (enum).
El comando "enum" permite construir tipos de datos con valores propios, inamovibles durante la ejecuci�n del programa.

Su sintaxis ser�:

enum <nombre del tipo> {[termino1],<termino2>,<termino3>,...}(nombre de la variable);

As� pueden ser enum:

enum ParElem {electron, foton, boson, meson}Particulas;
enum Formas{circulo,rectangulo,linea}Figuras;
enum Tinte {rojo,azul,verde,amarillo}colores;

Pueden ser representados por las palabras entre par�ntesis, y tambi�n pueden tomar valores num�ricos.

De esta forma, son v�lidas las siguientes asignaciones:

Particulas = electron;
Particulas = 1; //Significa que "Particulas" toma el valor "foton"
colores = azul;
Figuras = 0; //Significa que Figuras toma el valor "circulo".

Por otra parte, podemos alterar el orden en que se van numerando los t�rminos dentro de las llaves

enum componentes{diodos, transistores=2, compuertas = 4, memorias = 6}dispositivos

De esa forma "dispositivos" tomar�a num�ricamente los valores 0,2,4,6.

Si hubi�ramos numerado solamente uno de los t�rminos, a partir de �l se retomar�a la numeraci�n ascendente normal.

enum artefactos {fuentes=1,osciloscopios, puentes}equipos;

Esto significa que "equipos" podr�a tomar los valores 1,2,3.

Hay que significar que los t�rminos que asignamos a las variables de tipo enum no son cadenas de caracteres, sino nombres que el compilador codifica y ,por tanto, no pueden ser el resultado de entradas o ser utilizados para la salida de datos.

cin >> equipos; //PROHIBIDO
cout << dispositivos //PROHIBIDO

La utilidad de estas variables viene dada por la claridad que confieren a los programas y porque pueden ser tratadas como enteros y por tanto utilizadas en los switch.

Pueden existir en un enum hasta 255 valores diferentes.

Como tienen valores asignados pueden ser sumados, esto es:

colores = azul + verde; //colores tendr� ahora el valor 3 ,esto es,amarillo.

Como los enum definen al mismo tiempo tipos de datos, podemos tener:

Formas imagen,silueta;

De manera que ahora:

imagen = circulo;
silueta = linea;
imagen = silueta;

El typedef:

Cuando se quiere crear un tipo de dato propio, el comando utilizado es:

typedef.

A continuaci�n de �l tendremos el nombre del nuevo tipo de dato y enseguida el tipo de dato que queremos crear.

Un caso t�pico son los arreglos de datos, as�:

typedef char arreglo[25];

A partir de esta l�nea de programa arreglo es un tipo de dato, y las variables pueden ser referidas a �l como con cualquier otro tipo de dato. As�,

arreglo cadena,datos,nombres;

Ahora "cadena", "datos", "nombres", son variables que contienen 25 valores de tipo char.

En cualquier lugar que se use un tipo de dato b�sico de C++ puede ser utilizado el nuevo tipo de dato creado.

Las estructuras (struct).

Hasta aqu� se ha trabajado en forma aislada con los diferentes elementos que conforman un cuerpo u objeto determinado.

Pero, por ejemplo, si vamos a trazar un circulo, este tiene las coordenadas de su centro, el radio y el color con que se va a pintar.

Si nos referimos a una persona ser�a conveniente tener, en un solo dato, su nombre, edad y sexo ;y si estamos describiendo experimentos ser�a conveniente tener reunidos datos tales como: arreglo de corriente-voltaje, tipo de material, forma de obtenci�n.

Para todo esto C++ ofrece estructuras de datos que se forman a partir de los datos primarios del propio C++, o de tipos de datos creados previamente por el programador.

As�, podemos tener:

typedef struct Circulo{float X,Y;
int Radio;
COLORS color;
};

Ahora tenemos un nuevo tipo de dato Circulo que a su vez agrupa en su interior otros cuatro datos: dos de tipo float "X","Y"; uno de tipo int , que es el "Radio", y "color" que es un enum que trae definido C++ en su biblioteca "Graph.h". Todos forman una unidad que se puede mover conjuntamente cada vez que se trabaja con una variable de tipo Circulo.

Se pod�a haber prescindido del comando typedef , pero por homogeneidad en la escritura, se mantiene.

Para declarar las variables con este nuevo tipo de dato, se act�a como de costumbre:

Circulo Redondo,Mayor,Inicial;

Con lo que tenemos tres variables "Redondo", "Mayor" e "Inicial", de tipo Circulo.

Para referirnos a uno de los datos internos tendremos la siguiente sintaxis:

typedef struct alumno{char nombre[15];
                                 char apellidos[25];
                                 int edad;
                                 float salario;};
alumno estudiante,aprendiz,colaborador;
estudiante.nombre = "Enrique";
estudiante.apellidos = "Hern�ndez P�rez";
aprendiz.edad = 22;
colaborador = estudiante;

Veamos otro ejemplo de struct.

typedef float arreglo[100][100];
enum TipoMat {silicio,germanio,galio};
typedef struct Experimento{arreglo Valores;
                                        TipoMat Material;
                                        char FormaObt[25];
                                       };

Como se explic� en el ep�grafe referido al typedef, el nuevo tipo de dato puede ser utilizado dondequiera que se use un tipo de dato original de C++.

Experimento SerieDeExp[100];
float Funcion ( Experimento &UnIntento);
Experimento Llenar(Experimento &ConjuntoDe);

Hemos desarrollado en este ep�grafe de estructuras la forma en que funciona este tipo de dato pero debemos llamar la atenci�n al hecho de que al utilizar la programaci�n orientada a objetos las estructuras no son utilizadas debido a que la clase se convierte en la forma correcta de agrupar los valores y los objetos en las variables que los contienen.

Los punteros

Los apuntadores son variables que contienen como informaci�n una direcci�n de memoria donde est� situado el valor del tipo de dato con que fue definido el puntero.

Su sintaxis es:

int *p;

esto indica que p es una variable que va a guardar una direcci�n en la que puede estar un dato entero. Pueden existir punteros para cualquier tipo de dato, ya sea definido por C++ o por el programador.

Cuando se define una variable, de las que hemos estudiado hasta aqu�, estamos reservando en la memoria la cantidad de bytes que lleva el tipo de dato de la variable que estamos creando (ver tipos de datos C++):

De esta manera, cuando hacemos:

int valor;

se reservan en la memoria dos bytes, donde van a estar ubicados los datos que se sit�en en valor, (ver figura A)

wpeE.gif (3051 bytes)


Si ahora se asigna a la variable valor un dato, se tendr� en la memoria la situaci�n que aparece en la Fig. B.

valor = 232;

wpeF.gif (3059 bytes)

De esta forma se van situando los valores en las diferentes variables (posiciones de memoria).

Ahora, cuando se define un apuntador se reserva memoria para poner la direcci�n donde va a estar el dato que se quiere guardar , pero no se est� poniendo en ella nada, y el espacio que se reserva es siempre de dos bytes, con independencia del tipo de dato que se guardar� despu�s.

As�, al definir las variables AptdorEnt y AptdorFlot la estructura de la memoria es como muestran las figuras C y C’.

Image92.gif (8299 bytes)


Esto es, la primera salida ser� la direcci�n de memoria donde est� almacenado el n�mero 3. La segunda salida es el valor 3 que es el contenido de la posici�n de memoria apuntada por p.

El tama�o de una variable apuntador vendr� dado por el tipo de m�quina. As�, en una de 16 bits, ser� de 2 bytes; y en una de 32 bits, ser� de 4 bytes.

La sintaxis para situar los valores en las posiciones indicadas por los punteros es:

*AptdorEnt = 5;
*AptdorFlot = 0.2;

Ahora la situaci�n de la memoria vendr� descrita por la figura D, en que se ve c�mo los datos han ido a ocupar las posiciones reservadas por los punteros.

As� tenemos en las variables de tipo puntero las direcciones de memoria, y en la direcci�n de memoria indicada, los datos.

Resulta muy instructivo correr un programa como el siguiente:

void main()
{ int *p;
  p = new int;
  *p = 3;
  cout << "p = " << p;
  cout << "*p = " << *p;
}

La salida de este programa es:

p = 0x8eb31088
*p = 3

Cuando tenemos una estructura definida como puntero la forma de referirnos a los datos contenidos por ella ser�:

typedef struct Matriz {int nrcol, nr fila;
                                float col[nrcol],fila[nrfila];
                               }
Matriz *MatrizCuadrada;
MatrizCuadrada -> nrcol = 10;

Los punteros ofrecen otra posibilidad: la de reservar la zona de memoria posterior a la definici�n de la variable

Hasta ahora para definir un arreglo hab�a que poner el n�mero de datos que contendr�a el arreglo desde el momento en que se hace el programa.Con los punteros se puede esperar a la ejecuci�n del programa para conocer la cantidad de datos que hay que poner en el arreglo y ,en consecuencia, reservar ese espacio de memoria en el momento necesario y no antes.

El siguiente programa permite hacer este tipo de reserva de memoria de forma din�mica

  1. int * indice;
  2. int CtdadDatos;
  3. //solicitud de dato1
  4. cin >> CtdadDatos;
  5. indice = new int[CtdadDatos];

En la l�nea 10 se declara la variable tipo puntero "indice", en este caso es un puntero a un entero.

Con la l�nea 13 se capta desde el teclado el n�mero de datos que se guardar� y, por tanto, el tama�o que se necesita tenga el arreglo.

En la l�nea 14 con el operador new se habilita la memoria necesaria para que la variable "indice" contenga esa cantidad de datos.

Vemos aqu� otra importante propiedad de los punteros. Inicialmente "indice" es solamente un puntero a entero, mientras que en la l�nea 14, a trav�s del comando new ,se convierte en una variable que apunta a un arreglo de enteros, con una cantidad de valores dada por "CtdadDatos".

De esa forma, se habilita espacio en memoria para el arreglo que se necesita, y que solo se conoce su tama�o mientras que se ejecuta.

Si durante la operaci�n de reservaci�n de memoria se detecta que no existe un bloque que soporte la cantidad de datos que se piensa situar, el valor que devuelve es  NULL, de esa manera podemos saber si existe espacio para situar los datos que necesitamos.

if ((indice = new int[dato1])==NULL) cout <<"no hay memoria";

En los arreglos el nombre de la variable es un apuntador que solo se diferencia del apuntador gen�rico en que es constante, pues est� siempre referido al elemento 0 del arreglo; mientras que el apuntador gen�rico puede indicar a cualquier variable del tipo que fue definido.

Esto significa que en:

int *p
int arreglo[10];
p = arreglo; //VALIDO
arreglo = p; //NO VALIDO

no es v�lida ya que "arreglo" no puede dejar de apuntar al dato 0 del arreglo.

Otra caracter�stica de los apuntadores es que al aplicarles el operador ++ incrementa el valor que contienen en la cantidad necesaria para se�alar al siguiente dato .

El siguiente programa capta una cadena del teclado y la pone en pantalla aprovechando la aritm�tica de punteros.

void main()
{ char cadena[25]; char *PtroChar;
cin >> cadena;
PtroChar = cadena;
for (;!(*PtroChar=='\0');PtroChar++) cout << *PtroChar ;
}

En este programa aprovechamos la posibilidad que nos da "cadena", ya que al ser un puntero a un arreglo cin, puede situar all� una cadena de caracteres.

Como variable de ciclo utilizamos la propia direcci�n de memoria que tenemos en "PtroChar" y aplic�ndole el operador ++ vamos recorriendo la cadena. Como condici�n de fin de ciclo aprovechamos el hecho de que en C++ se pone autom�ticamente al final de la cadena un caracter nulo.

El siguiente programa le permite crear arreglos de forma din�mica captando la informaci�n en tiempo de ejecuci�n.

  1. typedef float Medicion[2];
  2. typedef struct Experimento {int NrIntento;
  3.                                          struct date fecha;
  4.                                          Medicion *Datos;
  5.                                         };
  6. void Captar(Experimento &ElExperimento)
  7. { int i;
  8.   int NrDatos;
  9.   cout << "Cuantos datos va a teclear: " ;
  10.   cin >> NrDatos;
  11.   ElExperimento.Datos = new Medicion[NrDatos];
  12.   getdate(&ElExperimento.fecha);
  13.   for(i=0;i<NrDatos;i++)
  14.      {cout << "Teclee el voltaje nr: " << i << " ";
  15.       cin >> ElExperimento.Datos[i][0];
  16.       cout << "\nTeclee la corriente nr: " << i << " ";
  17.       cin >> ElExperimento.Datos[i][1];
  18.       delay(100);
  19.       clrscr();
  20.      }
  21. }
  22. void main()
  23. { Experimento UnExperimento;
  24.   Captar(UnExperimento);
  25.   //resto del programa
  26.   delete UnExperimento.Datos;
  27. }

En la l�nea 15 pasamos por referencia un dato de tipo Experimento utilizando la capacidad de C++ de tratar los tipos de datos creados por el operador como los propios de C++; mientras, en la l�nea 20 reservamos de manera din�mica la memoria para la variable "Datos", situada dentro de la struct de Experimento.

En la l�nea 35 hacemos uso del delete para dejar libre la memoria que asignamos a "Datos".

El operador & que hemos venido usando para realizar la transferencia de par�metros por referencia, tambi�n nos indica "la direcci�n de la variable que viene a continuaci�n". De esta forma, lo podemos usar en forma combinada con los punteros. As�, podemos tener:

int *PtroInt, dato;
PtroInt = &dato;

Al igual que ocurre con las variables que son arreglos, en las que el nombre de la variable es un puntero, el nombre de las funciones es tambi�n un puntero, y por tanto podemos utilizarlo para transferir la direcci�n de ellas como un par�metro m�s de las funciones. Esto nos permite hacer operaciones gen�ricas que se puedan realizar a cualquier funci�n.

Un ejemplo t�pico es la b�squeda de ceros de una funci�n, lo que realizamos en el siguiente programa, poniendo como uno de los par�metros una referencia a una funci�n:

double parabola (double x)

{ double y;

  y = pow(x,2) - 2;

   return(y);

}   /*En esta funci�n pasamos a F como una variable puntero a una funci�n, que devuelve un tipo de dato "double", y tiene como        par�metro otro dato de tipo "double". En "ceros" se utiliza "f" para hacer referencia a una funci�n cualquiera.*/

void ceros (double f(double),double &x1, double &xf)

{ double delta; double x,y,y1; delta = fabs(xf-x1)/2.0; x = x1 + delta; y1 = f(x1);
  y = f(x);
  for ( ;fabs(y) > 1e-15;) //Aqu� fijamos el error con que vamos a calcular el cero.
    {delta /=2;
     if (y1*y>0 )
      {if (x1>x)
        { x-=delta;
          y1 = y;
        }
       else
        { x+=delta;
          y1 = y;
        }
      }
      else
      {if (x1>x) x+=delta;
         else x-=delta;
      }
     y = f(x);
   }
  x1 = y;
  xf = x;
}

void main()
{ double x1 = -10;
  double xf = 10;
  double s1 = 3*Pi/2;
  double sf = 5*Pi/2;
  clrscr();
  ceros(sin,s1,sf); //Aqu� calculamos los ceros de la funci�n "sin"
  cout << "valor final de la variable " << sf << " y de la funcion "<< s1 <<endl;
  ceros(parabola,x1,xf); //Aqu� calculamos los ceros de la funci�n "Parabola"
  cout << "valor final de la variable " << xf <<
              " y de la funcion " << x1 <endl;
  getch();
}

Este mismo ejemplo se desarrolla mas adelante utilizando clases abstractas en lugar de transferencia de parametros por referencia como aqu�.

arriba.jpg (2978 bytes) casa.jpg (31539 bytes)
Hosted by www.Geocities.ws

1