Estructuración de los datos
Programación estructurada
Los lenguajes de programación empezaron muy pronto a agrupar instrucciones reutilizables, conocidos como funciones. Las variables siguieron el ejemplo, aunque un poco más tarde.
La matriz se puede utilizar para procesar determinados algoritmos, siempre que los datos a procesar sean de un tipo uniforme (char, int...). Cuando los datos a procesar contienen diferentes tipos de información, es necesario utilizar varias matrices o una única que utilice un tipo void*. Hay que reconocer que esta solución no es recomendable.
En su lugar, definimos estructuras que agrupan varias variables llamadas campos. Estas variables existen en tantas copias como quiera, cada copia se llama instancia.
El lenguaje C++ tiene varias formas compuestas:
-
Las estructuras y las uniones, basadas en la C.
-
Las clases, que se tratarán en el capítulo dedicado a la programación orientada a objetos.
1. Estructuras
Las estructuras de C++, al igual que las estructuras de C, definen nuevos tipos de datos. El nombre dado a la estructura genera un tipo de datos:
struct Persona
{
char nombre[50];
int edad;
} ;
A partir de esta estructura Persona, ahora vamos a crear variables, siguiendo la sintaxis habitual de declaración que asocia un tipo y un nombre:
Persona angel, maria;
angel y maria son dos variables de tipo Persona. Como son tipos no primitivos (char, int...), decimos que son instancias de la estructura Persona. El término instancia nos recuerda que el nombre y la edad son características propias de cada persona.
Para acceder a los campos de una instancia se utiliza una notación especial:
angel.edad = 50; // edad de ángel
printf("%s",maria.nombre); // nombre de maría
Esta notación vincula el campo a su instancia.
2. Construir una estructura
Una estructura puede contener un número ilimitado de campos. Para los lectores que se inician en este tipo de programación y están acostumbrados a las bases de datos, resulta útil comparar una estructura con una tabla de una base de datos.
C++ |
SQL |
estructura |
tabla |
campo |
campo / columna |
instancia |
registro |
Por supuesto, cada campo de la estructura es de un tipo determinado. Puede ser de tipo primitivo (char, int...) o de tipo estructura. También se pueden obtener construcciones interesantes...
Estructuras y funciones
Son las funciones que operan sobre las estructuras. A menudo, las estructuras se declaran como variables locales en una función, para que se puedan utilizar como argumentos en otras funciones. Entonces, ¿cuál es la mejor forma de transmitirlas? Tenemos los tres métodos habituales: por valor, por dirección (puntero) o por referencia.
1. Pasar una estructura basada en valores como argumento
El modo por valor se recomienda si la estructura es pequeña y es necesario proteger sus valores contra cualquier modificación involuntaria, por parte de la función llamada. Este modo implica copiar todos los campos de la instancia en la pila, lo que puede llevar cierto tiempo y consumir recursos de memoria limitados. En el caso de funciones recursivas, el tamaño de la pila ya tiende a crecer rápidamente, por lo que no es buena idea sobrecargarla innecesariamente.
Sin embargo, esta copia evita cualquier efecto secundario, ya que es una copia de la estructura que se pasa.
void mostrar(Numero n)
{
switch(n.t_num)
{
case t_int:
printf("%d\t",n.val.num_i);
break;
case t_float:
printf("%f\t",n.val.num_f);
break;
case t_double:
printf("%f\t",n.val.num_d);
break;
}
}
2. Pasar una estructura por referencia como argumento
Este modo representa un avance considerable, ya que lo que se transmite es la referencia (dirección inalterable) de la estructura. Esta...
La librería estándar de C
Con las estructuras y la asignación de memoria, nos damos cuenta de que un lenguaje se debe basar en librerías del sistema para construir aplicaciones completas. C++ tiene su propia librería, pero muchos programadores siguen utilizando las funciones estándar de C.
1. Funciones comunes del lenguaje C <stdlib.h>
La librería estándar stdlib.h contiene funciones generales. Algunas funciones también se pueden declarar en otras cabeceras.
He aquí una lista que resume algunas funciones interesantes para el desarrollo cotidiano. Conviene consultar el libro El lenguaje C de Kernighan y Ritchie o la documentación suministrada con el compilador, para obtener una lista completa de funciones y sus firmas.
El libro Kernighan y Ritchie es la obra de referencia escrita por los creadores del lenguaje C. Sigue editándose y actualizándose a medida que evoluciona el lenguaje C.
Funciones |
Utilidad |
atoi, atof, strtod... |
Funciones para convertir entre un tipo de cadena y un tipo numérico. |
getenv, setenv |
Acceso a las variables de entorno del sistema. |
malloc, calloc |
Asignación de memoria. |
rand, abs |
Funciones matemáticas. |
La librería estándar stdlib también contiene macroinstrucciones basadas en la sintaxis #define:
Macro |
Utilidad |
__min, __max |
Proporciona los valores mínimo y máximo de dos argumentos. |
NULL |
Literalmente (void*)0. |
Un breve ejemplo muestra cómo utilizar el archivo:
char* lectura = new char[500];
printf("¿Número? ");
scanf("%s",lectura);
double numero = atof(lectura);
double pi = 3.14159265358;
printf("El número más grande es %2f\n",__max(numero,pi));
2. Cadenas <string.h>
El lenguaje C++ todavía gestiona sus literales de cadena en el formato char*, definido por el lenguaje C. Por lo tanto, es importante familiarizarse con las principales funciones de la librería string.h.
Funciones |
Utilidad |
memcpy, memcmp, memset |
Vista en memoria de cadenas de caracteres: copiar, comparar, inicializar. |
strcpy, strcmp, strcat, strlen |
Copiar, comparar, concatenar, longitud. |
strchr, strstr |
Búsqueda de caracteres y subcadenas. |
strlwr, strupr |
Conversión de mayúsculas/minúsculas. |
Veamos otro ejemplo para ilustrar el uso de esta librería:
/* ...
Ejercicios prácticos
He aquí un programa en C++ que combina estructuras y la librería estándar de C. Se trata de una pequeña base de datos que describe estrellas y constelaciones.
1. Las estructuras
El programa incluye dos estructuras, Estrella y Constelacion, que se instancian con los nombres proxima y gran_oso respectivamente.
/*
* estructuras de datos e instancias
*/
struct Estrella {
char* nombre;
float distancia_al;
};
struct Constelacion{
Estrella* estrellas;
int numero;
} gran_oso;
Estrella proxima;
2. La función de inicialización
La función de inicialización es una aplicación directa de la sintaxis para definir los valores de los campos de una estructura. Nótese también el uso del operador de conversión explícito (char*) para asignar a un char* un valor const char*.
// inicializar una constelación
void init()
{
printf("Initialización\n");
gran_oso.estrellas = new Estrella[3];
gran_oso.estrellas[0].nombre = (char*) "Alioth";
gran_oso.estrellas[0].distancia_al = 81;
gran_oso.estrellas[1].nombre = (char*) "Dubhe";
gran_oso.estrellas[1].distancia_al = 80;
gran_oso.estrellas[2].nombre = (char*) "Zeta";
gran_oso.estrellas[2].distancia_al = 78;
gran_oso.numero = 3;
}
3. Funciones de visualización
En primer lugar, está la función to_string(), que se encarga de representar una instancia en forma de cadena. Encontraremos este mecanismo como método (función integrada) en la mayoría de las clases C++.
// transforma una instancia de estrella en una cadena
char* to_string(Estrella estrella)
{
int tbuf = strlen(estrella.nombre) + 30;
char* buf = new char[tbuf];
sprintf_s(buf...