Estructuras, Typedef, Arreglos de Estructuras

Anuncio
Punteros
Un puntero es un tipo de variable que permite almacenar y manipular las direcciones de
memoria donde se encuentran almacenados los datos, en lugar de los datos mismos. El
manejo de punteros en el lenguaje C es muy poderoso y eficiente, aunque demanda
extremo cuidado en su utilización.
Bien utilizado hace que el código sea más compacto y eficiente; por el contrario, su uso
descuidado lleva a tener poca claridad y frecuentemente conduce a la pérdida del
control de la corrida, ocasionando un comportamiento indeseable del proceso.
El uso de punteros nos permite acceder al valor de una variable en forma indirecta. Para
esto se utilizan dos operadores:


& El operador "dirección de memoria de" que permite obtener la dirección de
memoria donde se aloja la variable a la cual precede ( &x es la dirección de
memoria de x).
* El operador "contenido de lo apuntado por" que permite acceder al valor
almacenado en la dirección de memoria que indica el puntero al cual precede (
*px es el contenido de lo apuntado por px)
Declaración
Las declaraciones de los punteros se realizan en forma indirecta, declarando el tipo de
dato que será apuntado por el mismo. Por ejemplo un puntero a un dato de tipo entero se
declara como:
int *px;
Sean además x e y dos variables del tipo entero. Existen dos operadores unarios para
usarlos con punteros:
Inicialización
Antes de poder ser utilizado, un puntero debe ser inicializado (como cualquier otra
variable). Para inicializar un puntero se puede utilizar:
a. la dirección de memoria de otra variable
int y=2, *py;
py = &y;
b. el valor de otro puntero ya inicializado.
int y=2, *py = &y, *ppy;
ppy = py;
1
Pasaje de argumentos por referencia
Supongamos que queremos realizar una función que recibe dos argumentos y que debe
retornar como resultado los valores invertidos de las dos variables. Sea por ejemplo la
siguiente implementación de esta función:
void Swap(int x, int y)
{
int Temp;
Temp = x;
x = y;
y = temp;
}
El efecto de esta función no es el deseado, ya que las variables son pasadas por valor, es
decir que lo que se modifica no son las variables originales, sino copias de ellas. La
forma de hacer esta operación es a través del paso de los argumentos por referencia. En
C esto se implementa haciendo uso de punteros:
void Swap(int *x, int *y)
{
int Temp;
Temp = *x;
*x = *y;
*y = Temp;
}
Como lo que se están pasando a la función Swap son en realidad direcciones de
memoria donde están alojadas las variables originales almacenadas, las modificaciones
que se realizan afectan estos valores. La invocación de esta función debe ser de la
forma:
Swap(&a,&b);
//Programa que suma un valor a una variable a través de un
//puntero.
#include <stdio.h>
#include <conio.h>
int main()
{
int a, *p;
a=5;
p=&a;
*p+=7;
printf("\nEl valor final de a es: %d", a);
getch();
}
2
Estructuras, Typedef, Arreglos de Estructuras
Una estructura es un conjunto de datos, posiblemente de tipos diferentes, agrupadas
bajo un mismo nombre, para hacer más eficiente su manejo.
Las estructuras ayudan a organizar datos complicados, particularmente en programas
grandes, ya que permiten tratar como una unidad a un conjunto de variables
relacionadas, en lugar de tratarlas como entidades independientes.
Un buen criterio de uso de estructuras establece que la definición de una estructura debe
ir acompañada por la definición de un conjunto de funciones que se encargan de realizar
la manipulación directa de la nueva entidad creada.
Definición de estructuras en Lenguaje C
Una estructura se define en lenguaje C a través de la siguiente sintaxis:
struct Nombre
{
tipo1 Campo1;
tipo2 Campo2;
...
tipoN CampoN;
};
La declaración
struct Nombre Var1;
Declara una variable del tipo "struct Nombre", esto es, el compilador reserva la cantidad
de memoria suficiente para mantener la estructura íntegra (es decir espacio para
almacenar Campo1, Campo2, ..., CampoN). Cuando se hace referencia a la variable
Var, se esta haciendo referencia a la estructura íntegra.
Inicialización de estructuras
Se puede inicializar una estructura externa o estática añadiendo a su definición la lista
de inicializadores:
struct Fecha
{
int Dia;
char Mes[10];
int Anio;
};
3
struct Fecha Hoy = {8,"Mayo",1991},
VarFecha;
...
VarFecha = Hoy;
La asignación VarFecha = Hoy copia la estructura integra Hoy en VarFecha. Cuando
dentro de los campos de una estructura aparecen arreglos de caracteres y se realiza este
tipo de asignación, se esta copiando también arreglos de caracteres.
Referencia de campos de una estructura
Un campo de una estructura se utiliza como una variable más. Para referenciar un
campo de una estructura se emplea el operador "."
Ejemplos:
Hoy.Dia = 24;
Hoy.Anio = 1991;
printf(“El mes es: %s\n",Hoy.Mes);
Ejemplo para verificar año bisiesto:
(Fecha.Anio % 4 == 0) && (Fecha.Anio % 100 != 0)) ||
(Fecha.Anio % 400 == 0)
Las estructuras se pueden anidar:
struct Person
{
char Name [NAMESIZE];
char Adress[ADRSIZE];
long ZipCode;
double Salary;
struct Fecha Nacimiento;
};
Para referenciar un campo de una estructura interna se utiliza la misma sintaxis,
referenciando campos internos dentro de campos:
struct Person Juan;
Juan.Nacimiento.Dia = 14;
4
Arreglos de Estructuras
Es posible agrupar un conjunto de elementos de tipo estructura en un array. Esto se
conoce como array de estructuras:
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
};
struct trabajador fijo[20];
Así podremos almacenar los datos de 20 trabajadores. Ejemplos sobre como acceder
a los campos y sus elementos: para ver el nombre del cuarto trabajador, fijo[3].nombre;.
Para ver la tercera letra del nombre del cuarto trabajador, fijo[3].nombre[2];. Para
inicializar la variable en el momento de declararla lo haremos de esta manera:
struct trabajador fijo[20]={{"José","Herrero
Martínez",29},{"Luis","García Sánchez",46}};
Typedef
El lenguaje 'C' dispone de una declaración llamada typedef que permite la creación
de nuevos tipos de datos. Ejemplos:
typedef int entero;
/* crear un tipo de dato llamado entero */
entero a, b=3;
/* declaramos dos variables de este tipo */
Su empleo con estructuras está especialmente indicado. Se puede hacer de varias
formas:
Una forma de hacerlo:
struct trabajador
{
char nombre[20];
char apellidos[40];
int edad;
5
};
typedef struct trabajador datos;
datos fijo,temporal;
Otra forma:
typedef struct
{
char nombre[20];
char apellidos[40];
int edad;
}datos;
datos fijo,temporal;
Ejemplos:
1.- Veamos antes que, además de int, char y float, otros tipos de datos simples son las
enumeraciones o tipos enumerados, donde se listan las constantes del tipo. Este tipo se
declara con la palabra reservada enum, como un conjunto de constantes enteras
representadas por identificadores. Por ejemplo:
enum ComunidadUniversitaria
{obrero,administrativo,estudiante, profesor};
Los valores de una enumeración se inician con 0 a menos que se inicialice en
otro valor, y se incrementan en 1. En el ejemplo anterior, los identificadores son
definidos automáticamente en los enteros 0 a 3. Otro ejemplo, donde los identificadores
tienen valores del 1 al 6 es:
enum Deportes
{Futbol=1,Beisbol,Tenis,Basketball,Natación,SoftBall};
Los identificadores en una enumeración deben ser únicos. Los nombres de las
constantes no pueden ser modificados en el programa.
/* Usando tipo enumerado */
#include <stdio.h>
enum meses {ENE = 1, FEB, MAR, ABR, MAY, JUN,
JUL, AGO, SEP, OCT, NOV, DIC};
main()
{
enum meses meses;
char *mesNombre[] = {"", "Enero", "Febrero", "Marzo",
6
"Abril", "Mayo", "Junio", "Julio",
"Agosto", "Septiembre", "Octubre",
"Noviembre", "Diciembre"};
for (meses = ENE; meses <= DIC; meses++)
printf("%2d%11s\n", meses, mesNombre[meses]);
getch();
}
2.- Además de los tipos simples, C provee los tipos estructurados los cuales permiten
construir estructuras de datos más complejas. Entre ellos están los arreglos. Los
arreglos ocupan espacio en memoria. En la declaración se especifica el tipo de cada
elemento y el número de elementos requerido por cada arreglo. Para declarar un arreglo
se escribe lo siguiente
<nombre_de_tipo> <identificador>[<tamaño>];
Un ejemplo de esta declaración sería: int s[6];
Con los arreglos se pueden construir y manipular colecciones de datos del
mismo tipo como si formaran un solo objeto. Una analogía común con los arreglos son
los vectores, los cuales son una colección de elementos del mismo tipo (por ejemplo
Reales) que tiene un tamaño y forman un solo objeto el vector. La declaración del tipo
vector sería
typedef float Vector[100];
En esta declaración se puede observar que el vector está formado por 100
elementos todos reales. La declaración de la variable sería
Vector v1;
El uso de un arreglo es como el de cualquier variable, pero debemos indicar cuál
es el elemento que queremos accesar. Esto se realiza a través del índice del arreglo, el
cual indica la posición del elemento a usar. Las posiciones en C, siempre empiezan en
cero (0) y terminan en la posición <tamaño> - 1. En el caso de los vectores, para
7
obtener el valor de la posición 20 y colocarlo en una variable real, se puede hacer en
algunas formas equivalentes como
y=v1[19];
i=19;
y=v1[i];
i=1;
y=v1[20-i];
Note que en el índice del arreglo (señalado entre corchetes) podemos colocar
una constante, una variable o una expresión, siempre que sean de tipo entero que el
subrango indicado en la definición del arreglo.


Algunos cuidados que debemos tener con los arreglos son:
el índice no debe tomar valores fuera del subrango, por lo cual al usar variables o
expresiones dentro del índice se debe hacer con precaución;
el tamaño de los arreglos está limitado por la memoria disponible.
3.- Una de propiedades de los tipos estructurados en C es que éstos pueden ser definidos
en función de otros tipos estructurados. Es así como, el tipo de los elementos de un
arreglo podría ser un arreglo. En C se simplifican estas construcciones en lo que se
denominan arreglos bidimensionales los cuales son análogos con las matrices.
La definición de una matriz quedaría
float Matriz [100][150];
En este caso se dice que la matriz (arreglo bidimensional) es de tamaño 100x150
y sus elementos son números reales.
Después de la declaración de la variable m como Matriz se puede usar dentro de
expresiones a través de dos índices, uno por cada dimensión del arreglo, como por
ejemplo
m[10][35] = m[10][36]+m[11][35];
4.- Otro tipo estructurado provisto por C son las estructuras o struct. Estos permiten
manipular elementos de tipos diferentes como si formaran un solo objeto. A estos
elementos se les denomina campos de la estructura. Las estructuras se definen usando la
siguiente instrucción:
struct <nombre_del_tipo> {
<tipo> <nombre_campo>;
...
<tipo> <nombre_campo>;
} <lista de identificadores>;
Para usar una estructura debe especificarse todo el camino donde se localiza el
campo. Por ejemplo, dada la definición de un tipo estructura que almacene una fecha
typedef struct Fecha {
int día;
enum mes {ene,feb,mar,abr,may,jun,
8
jul,ago,sep,oct,nov,dic};
int año;
}Tipo_fecha;
Se puede asignar una variable con la fecha de cumpleaños de la siguiente
manera:
Tipo_Fecha cumpleaños;
...
cumpleaños.día = 06;
cumpleaños.mes = jul;
cumpleaños.año = 1996;
Como se mencionó anteriormente, todo tipo estructurado puede estar definido en
función de otros tipos estructurados. Por ello, se pueden construir estructuras de
estructuras, arreglos de estructuras, estructuras de arreglos y así sucesivamente.
Ejercicios:
1.- Usando tipos enumerados, arreglos y estructuras, dé definiciones de tipos para:
a) Colores primarios
b) Días de la semana
c) Deportes acuáticos
d) Vocales y consonantes
e) Cargos de profesores: instructor, asistente, asociado y titular
f) Meses del año
g) Trimestre Ene-Abr 94
h) Mayúsculas y minúsculas
i) Minoría de Edad
Ejemplos de Estructuras:
a. Defina un tipo de datos llamado TFECHA, con los campos adecuados
para almacenar el día, mes y año de las fechas.
typedef struct {
int dia;
int mes;
int año;
} TFECHA;
b. Defina un tipo de datos llamado TCD con campos adecuados para
guardar el nombre del álbum, el cantante o grupo, la fecha que lo
adquirió (que debe ser del tipo TFECHA), y el tipo de música (Pop,
Rock, etc.).
9
typedef struct {
char Album[15];
char Cantante_Grupo[15];
char Tipo_Musica[15];
TFECHA Fecha_Ad;
} TCD;
c. Declare un arreglo MisCDs de tamaño 100 de tipo TCD.
TCD
MisCDs[100];
d. Asigne en la primera posición del arreglo MisCDs, los datos
correspondientes a su álbum musical favorito (si no recuerda la fecha en
que lo adquirió, puede poner cualquiera)
strcpy(MisCDs[0].Album,“Romances”);
strcpy(MisCDs[0].Cantante_Grupo,“Luis Miguel”);
strcpy(MisCDs[0].Tipo_Musica,“Bolero”);
MisCDs[0].Fecha_Ad.dia = 30;
MisCDs[0].Fecha_Ad.mes = 10;
MisCDs[0].Fecha_Ad.año = 1996;
Funciones
1. Escriba una función Norma que calcule y retorne la norma de un vector de
dimensión n. La función debe recibir como parámetros el vector (cuyos
componentes deben ser números reales) y un entero n que indique la dimensión
del mismo. Recuerde que la norma de un vector se define como la raíz cuadrada
de la suma de los cuadrados de sus componentes.
double Norma(float v[], int n)
{
double SumCuad = 0;
int i;
for(i=0; i < n; i++)
SumCuad += v[i]*v[i]; // Calculo la suma de los
cuadrados
10
return(sqrt(SumCuad));
cuadrada
}
// Calculo y retorno la raiz
2. Escriba el un procedimiento LeerDatos que recibe un vector y su dimensión, y
pide al usuario los datos para llenar las componentes del vector.
void LeerDatos(float v[], int n)
{
double SumCuad = 0.0;
int i;
for(i=0; i < n; i++) {
printf("Introduzca elemento %d: ",i);
scanf("%f",&v[i]);
}
}
3. Escriba la función main que declare las variables necesarias, llame al
procedimiento LeerDatos con una dimensión de vector de 100, luego calcule la
norma de dicho vector usando la función Norma, y la imprima como resultado.
void main()
{
float vector[3];
int i;
LeerDatos(vector,3);
printf("Norma = %f ",Norma(vector,3));
}
Escriba las instrucciones en C que realicen lo que se les pide, solo las instrucciones y no
el programa completo
2. Vectores y Matrices:
a. Sea Ciudad un arreglo de caracteres que ya ha sido inicializado, escriba
las instrucciones necesarias para:
i. Imprimir el contenido del arreglo Ciudad.
printf(“%s”, Ciudad);
ii. Imprimir el valor del séptimo carácter de Ciudad
printf(“%c”, Ciudad[6]);
11
iii. Asignar la constante Barquito al arreglo Ciudad
strcpy(Ciudad, “Barquito”);
b. Sea A una matriz de números reales de 1000 x 500, la cual ya tiene
asignados valores en sus componentes. Calcule e imprima el promedio
de los elementos correspondientes a la quinta columna de la matriz
int i;
double sum = 0;
for(i=0; i<1000;i++)
sum += A[i][4];
printf(Promedio = %f, sum/1000.0)
c. Declare una matriz M de enteros de dimensiones 100 x 200, y luego
asígnele los siguientes valores:
01010101…
10101010…
01010101…
10101010…
etc.
int M[100][200], i, j;
for(i=0; i <10; i++) {
if (i%2 == 0) { // Filas pares comienzan en
cero
for(j=0; j <20; j+=2) {
M[i][j]= 0;
M[i][j+1]= 1;
}
}else { // Filas impares comienzan en uno
for(j=0; j <20; j+=2) {
M[i][j]= 1;
M[i][j+1]= 0;
}
}
}
12
Descargar