![]() |
![]() |
![]() |
![]() |
Tipos de datos propios
arreglos
enum
typedef
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)
![]() |
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;
![]() |
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.
![]() |
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
|
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]; 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.
|
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); |
Este mismo ejemplo se desarrolla mas adelante utilizando clases abstractas en lugar de transferencia de parametros por referencia como aqu�.
![]() |
![]() |