estructuras estaticas

53
ESTRUCTURAS ESTÁTICAS En este capítulo trataremos sobre la utilización de las estructuras de datos complejas que el lenguaje C pone a disposición del programador para el tratamiento de la información. Llamamos estructuras de datos a conjuntos de informaciones simples que son tratables como un todo, al mismo tiempo que cada uno de sus elementos es identificable individualmente de algún modo. Las denominaremos estáticas para distinguirlas de aquellas estructuras de datos o formas de almacenamiento interno que se basan en la utilización dinámica de memoria. Vamos a hablar en este capítulo solo de aquellas estructuras de datos cuyo tamaño no varía a lo largo de toda la ejecución del programa. En C, algunos de los asuntos que trataremos en este tema están muy directamente relacionados con los punteros, por lo que resultará conveniente repasar los detalles de ese concepto antes de comenzar el estudio de este capítulo. 5.1 ARRAYS Un array es una colección de datos del mismo tipo -exceptuando el tipo void- que se referencian por un nombre común. Este conjunto de variables se almacenan en posiciones consecutivas de memoria; la dirección más baja corresponde al primer elemento y la más alta al último. Diagrama de un array en memoria: dato I dato 2 dato 3 dato 4 dato 5

Upload: cristian

Post on 11-Jun-2015

13.535 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: ESTRUCTURAS ESTATICAS

ESTRUCTURAS ESTÁTICAS

En este capítulo trataremos sobre la utilización de las estructuras de datos

complejas que el lenguaje C pone a disposición del programador para el

tratamiento de la información.

Llamamos estructuras de datos a conjuntos de informaciones simples que son

tratables como un todo, al mismo tiempo que cada uno de sus elementos es

identificable individualmente de algún modo.

Las denominaremos estáticas para distinguirlas de aquellas estructuras de

datos o formas de almacenamiento interno que se basan en la utilización dinámica

de memoria. Vamos a hablar en este capítulo solo de aquellas estructuras de

datos cuyo tamaño no varía a lo largo de toda la ejecución del programa.

En C, algunos de los asuntos que trataremos en este tema están muy

directamente relacionados con los punteros, por lo que resultará conveniente

repasar los detalles de ese concepto antes de comenzar el estudio de este

capítulo.

5.1 ARRAYS

Un array es una colección de datos del mismo tipo -exceptuando el tipo void- que

se referencian por un nombre común. Este conjunto de variables se almacenan en

posiciones consecutivas de memoria; la dirección más baja corresponde al

primer elemento y la más alta al último.

Diagrama de un array en memoria:

dato I dato 2 dato 3 dato 4 dato 5

Page 2: ESTRUCTURAS ESTATICAS

Los arrays pueden ser unidimensionales o multidimensionales. Un array de

una dimensión, también llamado normalmente vector, es una lista simple de

elementos que se pueden manejar conociendo su position relativa. Un array de

dos dimensiones podríamos imaginarlo como una tabla en la que los elementos

están distribuidos en filas y columnas. Para visualizar un array de tres

dimensiones utilizaríamos la imagen de un cubo de elementos, compuesto por

varias capas subdivididas a su vez en filas y columnas. Con arrays de más

dimensiones ya no resulta fácil establecer una analogía visual.

5.1.1 Declaración de un array

La declaración de un array incluye el tipo base de los elementos que lo

componen, el nombre que se le asigna y, entre corchetes, el tamaño de su o sus

dimensiones. Son los corchetes los que le indican al compilador que se trata

de un array para el que debe reservar el correspondiente espacio en memoria.

El tamaño de la primera dimensión puede omitirse en ciertas circunstancias: si se

inicializa el array en el momento de la declaración —como veremos más

adelante—, en la declaración como parámetro formal de una función o cuando se

hace referencia a un array declarado en alguna otra parte del programa.

Son ejemplos válidos de declaraciones:

int vector [1200]; /* declara un array llamado vector de 1200 elementos enteros */

char texto [40]; /* declara un array de 40 elementos de tipo carácter */

extern int vector [ ]; /* declaración del array vector; sus elementos son de tipo int y

la definición actual del vector tiene que estar hecha en otro fichero

fuente */

#define TAM 6

int a[2][3][4][5][TAM]; /* declaración de un array de cinco dimensiones */

Como vemos en este último ejemplo, el tamaño de una de las dimensiones puede

venir determinado tanto por un literal como par un identificador creado con una

Page 3: ESTRUCTURAS ESTATICAS

directiva define, siempre que sea entero. Nunca se podrá utilizar para este fin

una variable.

Una vez realizada la declaración, ya podemos referirnos a los valores

contenidos en el array simplemente indicando su nombre y, entre corchetes, su

ordinal dentro del conjunto. Este número de orden indicará al compilador cuantos

elementos tendrá que desplazarse tomando como referencia la dirección

inicial del array, es decir, servirá para que el compilador construya las

correspondientes operaciones de aritmética de punteros. Este desplazamiento

por tanto, desde el 0 para el primer elemento hasta n-1 para el último, siendo n

el número total de elementos del array.

Para el programador, los arrays estarán compuestos por elementos simples y

deberá preocuparse solo de utilizar un índice correcto en cada momento, pero no

de calcular las posiciones de esos elementos en la memoria principal. Dependiendo

del tipo base, como ya sabemos, los desplazamientos provocados por los índices

utilizados variarán, pero serán calculados por el compilador. Si el tipo de

datos es int, para avanzar un elemento probablemente tendrá que avanzar

dos bytes, pero por ejemplo, el tipo base es una estructura compleja, avanzar

un elemento significará para el compilador desplazarse en la memoria quizás a lo

largo de cientos de bytes.

Podemos concluir entonces que para el compilador el nombre del array es un

puntero constante que se corresponde con la dirección donde empiezan a

almacenarse sus elementos y a partir de ella tendrá que desplazarse utilizando

los índices introducidos por el programador. Estos índices podrán ser tanto una

constante como una variable o una expresión de tipo entero.

Habrá que tener un especial cuidado en el tratamiento de los índices porque

algo que no hará el compilador es comprobar si se encuentran dentro de los

límites de los arrays. Un código como el siguiente dará un resultado impredecible:

Page 4: ESTRUCTURAS ESTATICAS

int vector[5] = {4, 56, 7, 90, 4}; /*vector inicializado como veremos mas adelante */

printf("%d\n", vector[ 0]);

El compilador realizará el desplazamiento en memoria correspondiente a la

ocupación de diez números enteros, desde la dirección inicial del vector. En esa

posición existirá algún valor sin significado para nuestro programa, pero que se

presentará en la pantalla.

5.1.2 Inicialización de un array numérico

El lenguaje C permite la inicialización de arrays en el momento de

declararlos, introduciendo en esta declaración una lista de constantes

separadas por comas cuyo tipo sea compatible con el tipo base del array.

Para el caso de un array de números enteros haríamos:

int i[4] = {2, 4, 6, 8};

Los arrays multidimensionales se inicializan exactamente igual que los

vectores. Los valores se van introduciendo siguiendo el orden de las

dimensiones, por lo que sería recomendable editarlo del modo más claro posible.

Por ejemplo, será preferible introducir

int valores[2][3]={

5, 9, 32,

27, 6, 81};

o bien

int valores[2][3]= {{5, 9, 32}, {27, 6, 81}};

que la más confusa

int valores[2][3]={5, 9, 32, 27, 6, 81};

Cuando se inicializa un array de enteros con menos números que los que podría

albergar, la mayoría de los compiladores rellenarán el espacio restante con ceros.

Page 5: ESTRUCTURAS ESTATICAS

Así las siguientes inicializaciones serian equivalentes:

int enteros[10]={5, 10, 15, 20, 25};

int enteros[10]={5, 10, 15, 20, 25, 0, 0, 0, 0, 0};

En la mayoría de las ocasiones, la inicialización de arrays puede resultar una labor

tediosa si el programador tiene que calcular el espacio que necesita para

almacenar todos los valores a guardar. Afortunadamente existe la posibilidad de

hacer una inicialización de arrays no delimitados y dejar que sea el compilador el

encargado de calcular el espacio necesario para almacenar los valores. Con este

tipo de inicialización el programador se evita la preocupación de introducir

accidentalmente un número erróneo en la reserva de espacio de memoria para el

array.

Seria correcta, por tanto, la siguiente declaración:

int c[ ][3] = {10,12,14,

16,18,20}

El uso de inicializaciones no delimitadas vale también para arrays

multidimensionales, aunque en este caso solo es posible dejar sin

especificar el tamaño de la primera dimensión. Sera necesario conocer a

priori el resto de índices para poder acceder correctamente a los datos.

En el ejemplo anterior, la segunda dimensión debe estar especificada para

que el compilador pueda saber que en este caso existen dos filas, cada una de

ellas compuesta por tres elementos.

Debemos tener presente que los valores de un array, sea de una o más

dimensiones, se almacenan en la memoria ocupando posiciones consecutivas. El

anterior array de dos dimensiones, una tabla, podríamos visualizarla como sigue:

Page 6: ESTRUCTURAS ESTATICAS

10 12 14 16 18 20

El compilador es capaz de distinguir las filas de ese array siempre que se declare

el número de columnas que contienen.

5.1.3 Inicialización de un array de caracteres

El caso de los arrays de caracteres en C es un poco especial. A diferencia

de otros lenguajes de programación éste no posee un tipo específico para

tratar las listas de caracteres en las que almacenar información alfanumérica.

No existe un tipo diferenciado sino que habrá que construir cadenas de

elementos de tipo char.

Para facilitar el tratamiento de estas listas de caracteres como un todo, bien

para saber su longitud, bien para copiarlas o bien para presentarlas en la

pantalla, es conveniente introducir en ellas un carácter especial que indique

su final. Así las funciones que trabajen con ellas podrán recorrerlas hasta

el final buscando ese carácter. Este detalle de funcionamiento es

fundamental para los programadores porque cuando reserven la cantidad de

posiciones para una cadena de caracteres deberán incluir una más para el

carácter nulo '\0', que indica su final.

De esta forma, una inicialización semejante a la que veíamos para los números

enteros podría ser:

char cadena[14]={ 'P', 'r', 'o', 'g', 'r', 'a', 'm', 'a', ' ', 'e', 'n', ' ', 'C', '\0'};

Queremos almacenar una cadena de 13 caracteres y por tanto reservamos

espacio para 14.

Afortunadamente el compilador de C nos proporciona un cómodo mecanismo para

facilitar estas engorrosas inicializaciones:

Page 7: ESTRUCTURAS ESTATICAS

char cadena[17] = "Programando en C";

En este caso, se está utilizando una cadena constante y el propio compilador se

encargará de añadirle la terminación nula, pero seguirá siendo responsabilidad

del programador reservar espacio suficiente para contenerla.

Pero también las cadenas de caracteres podrán ser inicializadas sin delimitar,

dejando que sea el compilador el que calcule el espacio necesario para

almacenar los caracteres y el nulo que marca el fin de la colección.

unsigned char error[] = "Error en el número tecleado";

Con este tipo de inicialización el programador se evita la preocupación de

introducir accidentalmente un número erróneo en la reserva de espacio de

memoria para el array.

Ejemplos:

255.

/*****************************************************************

inverso.c

Programa que introduce números reales en un array y los muestra en orden

inverso al de lectura.

**********************************************************************/

#include <stdio.h>

#define N 10

void main(void)

{

double num[N];

int i;

printf(―\n \nLECTURA DE %d NUMEROS REALES Y PRESENTACION EN

ORDEN INVERSO\n\n", N);

for(i=0; i<N ;i++){

printf(―\nNumero %d: ", i+1);

scanf("%lf",&num[1]);

}

Page 8: ESTRUCTURAS ESTATICAS

for(i=N-1; i>=0; i--)

printf(―%.2lf/‖,num[i]);

}

256.

/********************************************************************** enmedio.c

Programa que lee números enteros del teclado en un array. La entrada de

datos finalizara cuando se introduzca el número 0 y el resultado será el

número que se encuentra en la mitad de los leídos ( si son dos los que se están

en la mitad. entonces se escribirán ambos números).

************************************************************************/

#include <stdio.h>

#define TAM 15

void main(void)

{

int num[TAM], i=0, n;

printf("\nLECTURA DE %d NUMEROS ENTEROS COMO MAXIMO Y

VISUALIZACION DE LOS CENTRALES",TAM);

printf("\nN£mero %d (0=fin): ", i+1);

scanf("%d",&n);

while(n!=0 && i<TAM){

num[i++]=n;

if(i==TAM)

printf("\n Se ha llenado el array \n");

else{

printf("\nN£mero %d (0=fin): ", i+1);

scanf("%d",&n);

}

}

Page 9: ESTRUCTURAS ESTATICAS

if(i!=0)

if(i%2==0)

printf("\nElementos centrales: %d y %d\n",num[i/2-

1],num[i/2]);

else

printf("\nElemento central %d\n",num[i/2]);

}

5.1.4 Paso de arrays como argumentos

Comentamos ya en diversas ocasiones que el paso de argumentos a una función se

puede realizar por valor o por dirección. El lenguaje C no permite pasar como

argumento un array completo, es decir, no vamos a tener la posibilidad de

pasarlo por valor. Sera necesario entonces pasar como argumento un puntero al

array con el que queremos que la función trabaje. El lenguaje C nos facilita

esta operación haciendo que la simple invocación del nombre de un array

genere un puntero a su primer elemento.

Así, si tenemos un array de números enteros y un puntero a enteros

int enteros[10], *p;

las dos siguientes sentencias serán equivalentes:

p=&enteros[0];

p=enteros;

Naturalmente, ésta última será preferible porque contribuye a obtener un

código más legible.

Podríamos ya llamar a una función enviándole como argumento un puntero al

array, de cualquiera de estas dos formas:

función(&enteros[0]);

función(enteros);

Page 10: ESTRUCTURAS ESTATICAS

Vemos a continuación un sencillo ejemplo en el que se trabaja con un array de

números enteros que es enviado come argumento a dos funciones, una que le

introduce valores y otra que los presenta en pantalla.

257.

/**********************************************************************

pasoarra.c

Programa que prueba el paso de arrays entre funciones.

**********************************************************************/ #include <stdio.h>

#define MAX 10

void introducir_valores(int valores[ ]);

void ver_valores(int valores[ ]);

void main(void)

{

int valores [MAX];

printf("\n\nPRUEBA DEL PASO DE ARRAYS ENTRE FUNCIONES\n\n");

printf("Introduzca un máximo de %d números naturales (<0 para acabar)\n",

MAX);

introducir_valores(valores);

printf("Los valores introducidos fueron:\n");

ver_valores(valores);

}

/**********************************************************************

void introducir_valores(int *valores)

Función que recibe un puntero al array de enteros y modifica sus valores.

**********************************************************************/ void introducir_valores(int *valores)

{

int i=-1;

do {

i++;

printf("Valor natural %d: ", i);

scanf("%d", &valores[i]);

} while (i<MAX-1 && valores[i]>=0);

}

Page 11: ESTRUCTURAS ESTATICAS

/**********************************************************************

void ver_valores(int valores[ ])

Función que recibe un puntero al array de enteros y presenta en pantalla sus

valores.

**********************************************************************/ void ver_valores(int valores[ ])

{

int i=0;

while(i<MAX && valores[i]>=0) {

printf("Valor natural %d: %d\n", i, valores[i]);

i++;

}

}

Vemos en este ejemplo cómo las funciones reciben un puntero al vector y

trabajan despues directamente con sus valores, incluso modificándolos.

Cuando se hace la definición formal de las funciones que recibirán ese puntero,

existen tres posibilidades para escribir el parámetro formal: como un array

delimitado, como un puntero o como un array no delimitado. En el ejemplo

anterior serian equivalentes:

void introducir_valores (int valores[MAX])

void introducir_valores (int *valores)

void introducir _valores (int valores[]);

De hecho, e insistiendo en la idea de que el compilador de C no comprobara los

límites de los arrays, podríamos escribir la siguiente sentencia y el programa

seguiría funcionando correctamente:

void introducir_valores(int valores[5000])

Solo estamos informando a la función de que va a recibir un puntero a un

entero; el saber que se trata de un vector y el controlar sus límites serán tareas

Page 12: ESTRUCTURAS ESTATICAS

del programador.

Ahora podemos ver claramente cual es la relación directa que existe entre la

aritmética de punteros y la utilización de vectores. Cuando en un programa

nos referimos a un determinado elemento de un vector, el compilador deberá

sumar el valor del índice del elemento al puntero de inicio del vector, para

desplazarse así hasta el elemento que se quiere utilizar:

valores[3]=2; /* es lo mismo que *(valores+3)=2; */

Cuando lo que se utiliza coma argumento de una función es un array

multidimensional, será necesario que el parámetro formal proporcione al

compilador el tamaño de todas las dimensiones exceptuando, como máximo, la

primera. En realidad, estaremos pasando igual que antes un puntero al primer

elemento, pero el compilador necesitará esa información para poder

desplazarse par un conjunto de valores que residen contiguos en la memoria

principal.

Vemos un mínimo ejemplo que ilustra esta situación.

5.2 CADENA DE CARACTERES

La utilización más habitual de los arrays unidimensionales es, con diferencia, la

de almacenar tiras o cadenas de caracteres –denominadas strings en muchos

lenguajes de programación-. Por eso, y por algunas de sus particularidades, les

dedicamos este apartado.

Por lo que ya sabemos, podemos deducir que una cadena no será más que un array

de caracteres terminado con el carácter nulo, por lo que en la definición de una

cadena habrá que prever el espacio que ocupara este símbolo de fin.

Page 13: ESTRUCTURAS ESTATICAS

De entre las funciones definidas en el fichero de cabecera <stdio.h> veremos las

siguientes: gets, puts, sprintf y sscanf. Conoceremos también las funciones

strlen, strcat, strncat, strchr, strrchr, strstr, strcmp, strncmp, strcpy, strncpy,

strpbrk, strspn y strtok, definidas en el fichero <string.h> y strtod, strtol,

strtoul, atoi, atof y atol definidas en el fichero de cabecera <stdlib.h>.

Recordamos de nuevo que como C no comprueba los limites durante las

operaciones sobre arrays, el programador es el único responsable de prevenir los

posibles desbordamientos durante su uso. En el siguiente capítulo veremos una

función que previene los desbordamientos sobre arrays de caracteres.

/**********************************************************************

gets.c

Acepta caracteres de la entrada estándar para dejarlos en una cadena y después

presentarlos uno a uno en la pantalla.

**********************************************************************/

#include <stdio.h>

#define MAX 50

void main(void)

{

unsigned char cadena[MAX];

int i;

printf("\n\nUSO DE LA FUNCIÓN GETS\n\n");

printf("Introduzca una cadena de caracteres de %d caracteres como máximo: \n",MAX);

char* gets (char*s)

Esta función es una interesante alternativa a scanf para la introducción por la

entrada estándar generalmente el teclado –de cadenas de caracteres, porque

permite incluir el carácter espacio como carácter de nueva línea y , después

sustituirlo por \0 , dejando el resultado en el array s. si ocurre algún error

devolverá el valor NULL, en caso contrario devuelve un puntero al array s.

Page 14: ESTRUCTURAS ESTATICAS

if(gets(cadena)!=NULL)

for (i=0; cadena[i]; i++)

printf("%c", cadena[i]);

}

263.

/**********************************************************************

puts.c

presenta caracteres en la salida estándar.

**********************************************************************/

#include <stdio.h>

int main(void)

{

unsigned char cadena[ ]="Bienvenidos a nuestro programa de gestión";

printf("\n\nUSO DE LA FUNCIÓN PUTS\n\n");

puts(cadena);

}

264.

/****************************************************************************

sprintf.c

Da formato a un texto que se almacena en una cadena de caracteres.

int puts(const char*s)

Esta otra función es una alternativa a printf para la presentación de caracteres en la salida

estándar –generalmente la pantalla-.presenta la cadena de caractres s seguida del carácter de

nueva línea. Si ocurre algún error devolverá el valor EOF, en otro caso, un valor no negativo.

int sprintf (char*cadena, const char*formato, arg1, arg2, …)

Realiza el mismo trabajo de formatear textos que printf, pero el resultado

queda almacenado en la cadena de caracteres cadena.

int sscanf (const char*cadena*const char*formato,….)

Sirve al igual que scanf, para permitir una entrada con formato , pero en este

caso esa entrada es tomada del array cadena.

Page 15: ESTRUCTURAS ESTATICAS

****************************************************************************/

#include <stdio.h>

#define MAX 50

int main(void)

{

unsigned char destino[MAX], saludo[ ]="Bienvenido", nombre[MAX];

int dia=14;

printf("\n\nUSO DE LA FUNCIÓN SPRINTF\n\n");

printf("Introduzca su nombre (de menos de %d caracteres): ",MAX);

gets(nombre);

sprintf(destino, "%s, %s. Hoy es día %d ", saludo, nombre, dia);

puts(destino);

}

265.

/****************************************************************************

strlen.c

Presenta en pantalla la longitud de una cadena de caracteres tecleada.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

size_t strlen(const char*s)

Devuelve la longitud de la cadena de caracteres s, sin incluir el

character determinacion \0 . El tipo size_t está definido en string.h

como unsigned.

Page 16: ESTRUCTURAS ESTATICAS

void main(void)

{

unsigned char texto[MAX];

unsigned longitud;

printf("\n\nLONGITUD DE UNA CADENA DE CARACTERES\n\n");

printf("Introduzca una frase (de menos de %d caracteres) para calcular su longitud:

",MAX);

gets(texto);

longitud = strlen(texto);

printf("La longitud de la frase es %u\n", longitud);

}

266.

/****************************************************************************

strchr.c

Presenta la parte final de una cadena de caracteres a partir del car cter '@'.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

char*strchr(cons char*s, char c)

Devolverá un puntero a la primera posición dentro de la cadena s en que

aparece el carácter c, o un puntero nulo sino lo encuentra.

char*strrchr(cons char*s, char c)

Devolverá un puntero a la última posición dentro de la cadena s en que

aparece el carácter c o un puntero nulo sino lo encuentra.

Page 17: ESTRUCTURAS ESTATICAS

void main(void)

{

char texto[MAX],*puntero;

printf("\n\nEXTRACCION DE LA PARTE FINAL DE UNA CADENA A PARTIR DE

@\n\n");

printf("Introduzca una direcci¢n de correo electr¢nico:\n");

gets(texto);

puntero = strchr(texto, '@');

if(puntero)

puts(puntero);

}

267.

/****************************************************************************

strcat.c

Presenta en pantalla la unión de dos cadenas de caracteres tecleadas, separadas

por el carácter '+'.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

void main(void)

{

unsigned char texto1[MAX*2], texto2[MAX];

char*strcat( char*s, const char*t)

Esta función permite concatenar una copia de la cadena de caracteres t al

final de la cadena s. en la cadena a la que apunta el resultado devuelto s, se

eliminara el terminador de la primera cadena y se incluirá al final de la

concatenación.

char*strncat(char*s, const char *t, size_t n)

Concatena hasta n caracteres de la cadena t a la cadena s. Devolverá un

puntero a la cadena s después de añadir el delimitador de final \0 .

Page 18: ESTRUCTURAS ESTATICAS

printf("\n\nUNIÓN DE DOS FRASES\n\n");

printf("Introduzca la primera frase (de menos de %d caracteres):\n",MAX);

gets(texto1);

printf("Introduzca la segunda frase(de menos de %d caracteres):\n",MAX);

gets(texto2);

strcat(texto1, "+");

strcat(texto1, texto2);

printf("La frase resultante es: %s\n", texto1);

}

En este caso se hace necesario insistir en la idea de que el compilador del

lenguaje no comprobará los límites de los arrays, y, de ser necesario, ese trabajo

será responsabilidad del programador.

268.

/****************************************************************************

strstr.c

Encuentra la palabra “autor” dentro de una cadena de caracteres.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

int main(void)

{

unsigned char texto[ MAX],*puntero;

char*strstr( char*s, char*t)

Devolverá un puntero a la posición dentro de la cadena s en que

aparece la cadena t o un puntero nulo sino la encuentra.

Page 19: ESTRUCTURAS ESTATICAS

printf("\n\nBÚSQUEDA DE UNA CADENA DE CARACTERES DENTRO DE OTRA\n\n");

printf("Introduzca una frase (de menos de %d caracteres) que contenga:",MAX);

printf("\nel título de un libro, la palabra autor y el autor");

printf(":\n",MAX);

gets(texto);

puntero = strstr(texto, "autor");

if (puntero)

printf("%s\n", puntero);

else

printf("No se ha encontrado la palabra autor");

}

269.

/****************************************************************************

strcmp.c

Compara una cadena de caracteres tecleada con otra introducida en el programa.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 9

int strcmp( char*s, char*t)

Esta function compara alfabeticamente-segun el alfabeto ingles- dos

cadenas de caracteres y devuelve un valor entero que nos informa del

redultado: si la primera cadena es menos obtendremos un número

menor que 0, si es mayor, un numero mayor que 0 y si son iguales, un

0.

int strcmp( char*s, char*t, size_t n)

Compara solo n caracteres de la cadena s con la cadena t. el valor

devuelto dependerá de la comparación de igual forma que en la

función strcmp.

Page 20: ESTRUCTURAS ESTATICAS

int main(void)

{

unsigned char texto[MAX];

printf("\n\nADIVINAR UNA CONTRASEÑA\n\n");

printf("Introduzca una contraseña (de menos de %d caracteres): ",MAX);

scanf("%s", texto);

if (strcmp(texto, "invitado"))

printf("La contraseña no es correcta\n");

else

printf("Ha entrado usted en nuestro sistema.");

}

En este pequeño programa se utiliza la función strcmp para comparar una cadena

de caracteres introducida por el teclado con otra que sirve como clave. En caso

de coincidir, la función strcmp devolverá el valor 0 y se ejecutara la parte

correspondiente a la clausula else.

char*strcpy(char*s,char*t)

Copia el contenido de la cadena t –incluído el carácter \0 en que debe

estar acabada- en la cadena s y devuelve esta última. El tamaño de la

cadena destino s debe ser suficiente para contener la información que se le

copia.

Esta es, por tanto, la función con la que realizaremos las operaciones de

asignación de valores a las cadenas de caracteres. El operador de

asignación, '=', sólo se podrá utilizar en el momento de la inicialización de

la cadena, nunca posteriormente.

char*strncpy(char*s, char*t, size_t n)

Copia sólo n caracteres de la cadena t en la cadena s y añade también el

carácter \0 . Si la cadena t no tiene n caracteres, completará con

caracteres \0 .

Page 21: ESTRUCTURAS ESTATICAS

270.

/****************************************************************************

strcpy.c

Copia una cadena de caracteres en otra.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

int main(void)

{

char destino[MAX], origen[MAX];

printf("\n\nCOPIA DE CADENAS\n\n");

printf("Introduzca una cadena de caracteres (de menos de %d caracteres): ",MAX);

gets(origen);

strcpy(destino, origen);

printf("El destino (%s) y el origen (%s) deberían ser iguales", destino, origen);

}

char *strpbrk(char *s, char *t)

Devuelve un puntero al primer carácter de s que esté presente en t o NULL si no

existe ninguno.

size_t strspn(char *s,char *t)

Devuelve el número de caracteres del comienzo de s que están contenidos en t.

size_t strspn(char *s, char *t)

Devuelve el número de caracteres del comienzo de s que no están contenidos en

t.

char *strtok(char *s,char *t)

Función que sirve para encontrar las partes de s delimitadas por caracteres de t.

Normalmente se utilizará realizando llamadas sucesivas a esta función, que

devolverá en cada una de ellas un nuevo fragmento o token de s. en la primera

de esas llamadas se le indicará la cadena de la que se quieren extraer los

fragmentos, las sucesivas llamadas se realizarán utilizando NULL como primer

parámetro.

Page 22: ESTRUCTURAS ESTATICAS

271.

/****************************************************************************

strtok.c

Parte una cadena de caracteres en fragmentos separados por caracteres de otra.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

int main(void)

{

char entrada[MAX], *nombre, *ap1, *ap2;

char delimitadores[]={",.; "};

printf("\n\nUSO DE STRTOK\n\n");

printf("Introduzca su nombre y apellidos (de menos de %d caracteres): ",MAX);

gets(entrada);

nombre=strtok(entrada, delimitadores);

ap1=strtok(NULL, delimitadores);

ap2=strtok(NULL, delimitadores);

printf("\nNombre: %s, apellido 1º: %s, apellido 2º: %s. \n", nombre, ap1, ap2);

}

Page 23: ESTRUCTURAS ESTATICAS

Estas son algunas de las funciones que proporciona el lenguaje C para la

conversión de tipos. Las funciones strtod y strtol tienen una gran potencia y

versatilidad. Sin embargo, para las conversiones más habituales existen

funciones equivalentes de sintaxis más sencilla. Se trata de atoi, atol,y atof

cuyos prototipos se encontraran en el fichero stdlib.h.

double strtod(const char*s, char**resto)

Convierte la cadena de caracteres s en un número de tipo double.

La cadena s deberá adaptarse al siguiente formato exponencial (donde

los corchetes indican que el elemento es opcional):

[espacios][signo][digitos][.][digitos][e/E[signo]digitos].

La base que se eleva el exponente indicado después del signo e o E, es

10.

Si el valor introducido rebasa por arriba el rango del tipo double,

devolverá el valor HUGE_VAL, si lo rebasa por abajo, devolverá 0.

El puntero resto apuntara a la parte final de la cadena s que no se

adapta al formato numérico científico. Si no existe ese resto, su valor

será NULL.

long strtol(const char*s, char**resto, int base)

Convierte la cadena de caracteres s en un número de tipo long.

Su funcionamiento es muy semejante al de strtod, excepto en que

ahora podemos especificar la base.

Si la base está entre 2 y 36 esa será la base que se use para la

conversión.

Si base es 0, la base de conversión podrá ser 8, 10 o 16 dependiendo de

los caracteres iniciales de s: si son 0 entenderá que es octal y si son 0x

o 0X hexadecimal.

unsigned long strtoul(const char*s, char**resto, int base)

De funcionamiento semejante a strtol, convierte la cadena de

caracteres s en un número de tipo unsigned long.

Page 24: ESTRUCTURAS ESTATICAS

En el siguiente ejemplo utolizaremos la función atoi para convertir en un número

el argumento pasado a main como una cadena de caracteres.

272.

/****************************************************************************

atoi.c

Programa que transforma un argumento compuesto de caracteres en un entero.

Comprueba que le sea proporcionado un único argumento y, si es posible lo convierte

en un entero, presentando los números enteros comprendidos entre el 0 y el

argumento -1.

****************************************************************************/

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[ ])

{

int i;

int atoi (const char*s)

Función que convierte una cadena de caracteres s en un número

entero, siempre que sea posible.

Sería equivalente a (int) strtol (s,(char**) NULL, 10).

long atol (const char*s)

Función que convierte una cadena de caracteres s en un numero long,

siempre que sea posible.

Sería equivalente a strtol(s,(char**)NULL,10).

double atoff(const char**)

Función que convierte una cadena de caracteres s en un numero

double , siempre que sea posible.

Sería equivalente a strtod(s, (char**)NULL).

Page 25: ESTRUCTURAS ESTATICAS

if (argc==1)

printf("Este programa necesita un argumento y no se ha introducido ninguno.\n");

else

if(argc>2)

printf("Este programa necesita un £nico argumento y se han introducido

%d.\n", argc-1);

else

for (i=0;i<atoi(argv[1]);i++)

printf("%d\n", i);

}

273.

/****************************************************************************

unirnum.c

Unir cantidades numéricas (que no ocupen más de una línea) tecleadas en la línea de

comandos, para formar una nueva cantidad numérica.

****************************************************************************/

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[])

{

int i;

char numero[81]="";

double resultado;

if (argc <2)

printf("Error de sintaxis. Por lo menos hay que teclear un n£mero entero");

else

Page 26: ESTRUCTURAS ESTATICAS

{

for (i=1;i<argc;i++)

strcat(numero,argv[i]);

resultado=atof(numero);

printf("N£mero resultante: %.0lf",resultado);

}

}

5.4.3 funciones estándar de búsqueda

Igual que sucedia con el método de ordenación rápida , también disponemos de

una función para la búsqueda binaria, bsearch, que utiliza parámetros muy

semejantes, como veremos en el siguiente ejemplo.

282.

/****************************************************************************

buscarb.c

Búsqueda binaria en un array de enteros utilizando la función bsearch. El array en

el que se busca deberá estar ordenado ascendentemente.

****************************************************************************/

void *bsearch (const void* elemento, void*vector, size_t n,size_t ancho,

int(*cmp) (const void*, const void*))

La función bsearch necesita un puntero al elemento a buscar,otro puntero al vector en el

que realiza la búsqueda, el numero de elementos que componen la lista, el tamaño de

cada uno de esos elementos y una función que realice comparaciones entre pares de

elementos. Tal función deberá devolver un valor entero menor que 0 cuando el primer

argumento que se le proporciona sea menor que el segundo, un valor positivo cuando sea

mayor y 0 cuando resulten iguales.

La función bsearch devuelve un puntero al primer elemento que se corresponda con el

elemento buscado o NULL en caso contrario.

Page 27: ESTRUCTURAS ESTATICAS

#include <stdlib.h>

#include <stdio.h>

int nums[ ] = {5, 12, 31, 89, 127, 149};

int buscar(int elemento);

int orden (const int *dato1, const int *dato2);

int main(void)

{

int dato;

printf("\n\nBÚSQUEDA DE LA PRIMERA APARICIÓN DE UN ENTERO EN UN

ARRAY\n\n");

printf("Introduzca el dato a buscar: ");

scanf("%d", &dato);

if (buscar(dato))

printf("El valor %d se encuentra en el vector.\n", dato);

else

printf("El valor %d no se encuentra en el vector.\n", dato);

}

/****************************************************************************

int buscar(int elemento)

Función que realiza la llamada a bsearch y devuelve el resultado de la búsqueda.

****************************************************************************/

int buscar(int elemento)

{

Page 28: ESTRUCTURAS ESTATICAS

int *itemptr;

itemptr = bsearch (&elemento, nums, 6, sizeof(int), (int(*)(const void *,const void *))

orden);

return (itemptr != NULL);

}

/****************************************************************************

int orden (const int *dato1, const int *dato2)

Función que hace la comparación entre el dato buscado y cada uno de los elementos

del array de enteros. Esta función devolverá un número negativo si dato 1 es menor

que dato 2, un número positivo si es mayor y 0 en caso de igualdad.

****************************************************************************/

int orden (const int *dato1, const int *dato2)

{

return(*dato1 - *dato2);

}

5.5 TIPOS DEFINIDOS POR EL PROGRAMADOR

En este apartado veremos las diferentes formas que tiene el lenguaje C de

permitir la creación de tipos de datos propios del programador.

Resulta evidente que los tipos de datos básicos no cubren todas las posibles

necesidades en el tratamiento de información compleja. Por ello veremos ahora

diferentes agrupaciones de tipos básicos, la forma de emplearlas y su utilidad

dentro de los programas.

5.5.1 Estructuras

Cualquier lenguaje de programación tiene que proporcionar a los programadores

una herramienta para almacenar y tratar informaciones de tipos diversos que

formen un conjunto accesible bajo un único nombre. Esa es la función que

cumplen en C las estructuras.

Page 29: ESTRUCTURAS ESTATICAS

Podemos definir entonces una estructura como un conjunto de una o más

variables, del mismo o de diferentes tipos, que se agrupan bajo un único nombre

para facilitar su manejo.

Los ejemplos de la vida real que se podrían utilizar para ilustrar el concepto de

estructura son múltiples. Imaginemos que queremos almacenar y tratar la fecha

y hora de algún tipo de suceso. Podemos reservar variables individuales para el

día, el mes, el año, la hora, el minuto y el segundo, o bien podemos facilitar el

tratamiento creando una estructura que agrupe todos esos datos bajo un único

nombre, por ejemplo, momento.

struct momento { int dia,mes,año,hor,min,sec};

¿Cuál es entonces la ventaja de las estructuras? Que nos permiten manejar con

comodidad colecciones complejas de información heterogénea pero relacionada.

Por ejemplo, podemos hacer asignaciones de toda la estructura, copiarla o

pasarla como argumento de una función, pero también referirnos a cualquiera de

sus elementos individuales.

Una declaración de estructura como la anterior no es más que la introducción en

nuestro programa de un nuevo tipo de datos. La estructura momento pasa a

formar parte de los tipos posibles de variable junto con los proporcionados por C.

es decir, ahora podríamos declarar variables de tipo momento:

struct momento inicio,fin;

Esa etiqueta que le hemos puesto a nuestra estructura es opcional y podríamos

unir los dos pasos, definición de la estructura y declaración de variables, en uno

solo:

struct momento { int dia, mes, ano, hor, min, sec¡ inicio, fin;

La definición de la estructura no reserva espacio en memoria, se trata

simplemente de una indicación al compilador de cual es la forma y el contenido de

ese nuevo tipo de datos. Es la declaración de variables la que reserva finalmente

en memoria, un número de bytes igual como mínimo a la suma de bytes de cada

uno de los campos.

Page 30: ESTRUCTURAS ESTATICAS

Una estructura se podrá inicializar en el momento de la declaración igual que

cualquier otra variable.

struct momento suceso = {28, 6, 2005, 14, O, 0};

Las estructuras podrán contener, naturalmente, información de cualquiera de los

tipos de datos base o de otros creados por el programados Los arrays son, por su

extensa utilidad, elementos que casi siempre formarán parte de las estructuras.

struct empleado {

unsigned char nombre[20];

unsigned char apellidos [50];

unsigned char puesto[40];

int trienios;

int edad;

};

Decíamos antes que una de las ventajas de las estructuras es que podemos

combinar el acceso al conjunto de datos con la utilización individual de cada uno

de sus elementos. Para referirse a ellos existe el denominado operador punto,

que separará el nombre de la estructura del nombre del elemento al que

necesitamos referirnos. Por ejemplo:

struct empleado jefe 1 , jefe2; /* definimos dos variable*/

jefe 1. trienios = 6;

strcpy(jefel.puesto, "Director de Departamento");

gets(jefel. nombre);

También se pueden realizar asignaciones entre variables tipo estructura,

operación que no podía realizarse con arrays. Por ejemplo:

jefe2=jefel;

Page 31: ESTRUCTURAS ESTATICAS

Asignará los valores de los campos de la variable jefe1 a los campos de jefe2, es

decir, por ejemplo, el campo trienis de la variable jefe2 valdrá ahora 6.

Arrays de estructuras

Probablemente el uso más común de las estructuras es el de formar parte de un

array. Existirán innumerables problemas en los que el algoritmo necesite manejar

colecciones de elementos complejos, por ejemplo una lista de alumnos o un

conjunto de empleados. Como cada uno de esos elementos individuales tiene una

serie de características — nombre, edad, sueldo, etc. — que queremos almacenar

y que hacen referencia a un mismo objeto — en este caso información sobre una

persona — , constituirán una estructura, y el conjunto de elementos formará un

array de estructuras.

Si queremos declarar un array de estructuras deberemos definir primero la

estructura y después declarar una variable array de ese tipo.

Por ejemplo, para declarar un array de estructuras en las que sea posible

almacenar información sobre los veinte participantes en una competición,

deberíamos hacer:

struct competidor {

unsigned char nombre[26];

unsigned char apellidos[5 1 ];

int dorsal;

int edad;

struct competidor participantes [20];

Aprovecharemos este array de estructuras para construir un programa de

ejemplo que sirva para manejar la información sobre una competición deportiva.

Page 32: ESTRUCTURAS ESTATICAS

El programa permite simplemente introducir información sobre los participantes,

obtener un listado o borrar alguno de los valores almacenados.

Obsérvese cómo la función que introduce nuevos participantes en el array,

realiza previamente una llamada a otra función que busca el primer hueco en la

lista. De esta forma se ocuparán las posiciones dejadas por los participantes

borrados y la lista tenderá a no tener espacios intermedios libres.

En este ejemplo no se realiza ninguna validación de las entradas.

283.

/****************************************************************************

compiten.c

Sencillo programa que utiliza una array de estructuras para llevar el control de una

lista de participantes en una competición deportiva.

No se controla que se introduzcan números de dorsal repetidos.

****************************************************************************/ #include <stdio.h>

#include <string.h>

#define MAX 20 /* número máximo de participantes */

#define N 25 /* número máximo de caracteres para nombre y para apellido */

struct competidor {

unsigned char nombre[N+1];

unsigned char apellido[2*N+1];

int dorsal;

int edad;

};

struct competidor participantes[MAX]; /* se inicializa a valores nulos */

void introducir(void);

void borrar(void);

Page 33: ESTRUCTURAS ESTATICAS

void listar(void);

int buscar_libre(void);

int menu(void);

void main(void)

{

int opcion;

do {

opcion=menu();

switch(opcion){

case 1: introducir();

break;

case 2: borrar();

break;

case 3: listar();

break;

}

} while (opcion);

}

/****************************************************************************

int menu(void)

Función que presenta el menú de opciones y devuelve la seleccionada por el usuario.

****************************************************************************/ int menu(void)

{

int opcion;

Page 34: ESTRUCTURAS ESTATICAS

printf("\n\nCONTROL DE PARTICIPANTES EN UNA COMPETICIÓN DEPORTIVA\n\n");

printf("1. Introducir un participante\n");

printf("2. Borrar un participante\n");

printf("3. Lista de participantes\n");

printf("0. Salir\n");

do {

printf("Escoja una opcion (entre 0 y 3): ");

scanf("%d", &opcion);

} while(opcion<0 || opcion>3);

return opcion;

}

/****************************************************************************

void introducir(void)

Función que permite introducir un nuevo participante. Previamente hará una llamada

a la función buscar_libre para comprobar si existe espacio y, en ese caso, cuál es

la primera posición libre.

****************************************************************************/ void introducir(void)

{

int lugar;

unsigned char s[MAX];

char car; /* para vaciar el buffer del teclado */

lugar=buscar_libre();

if (lugar==MAX)

printf("No hay sitio en la lista\n");

else {

Page 35: ESTRUCTURAS ESTATICAS

scanf("%c",&car); /* elimina el \n almacenado en el teclado */

printf("Nombre (de menos de %d caracteres): ",N);

gets(participantes[lugar].nombre);

printf("Apellido (de menos de %d caracteres): ",2*N);

gets(participantes[lugar].apellido);

printf("Dorsal: ");

scanf("%d", &(participantes[lugar].dorsal));

printf("Edad: ");

scanf("%d", &(participantes[lugar].edad));

}

}

/****************************************************************************

int buscar_libre(void)

Función que encuentra la primera posición libre, si es que existe. ************************************************************************************/ int buscar_libre(void)

{

register int n;

for(n=0; n<MAX && participantes[n].nombre[0]; n++);

return n;

}

/****************************************************************************

void borrar(void)

Función que permite eliminar un participante colocando el valor nulo en el nombre.

****************************************************************************/ void borrar(void)

{

int dorsal, encontrado=0;

register int n;

printf("Dorsal del participante a eliminar: ");

Page 36: ESTRUCTURAS ESTATICAS

scanf("%d", &dorsal);

for (n=0; encontrado==0 && n<MAX; n++)

if (participantes[n].dorsal==dorsal) {

participantes[n].nombre[0]='\0';

encontrado=1;

}

if (encontrado)

printf("Participante borrado");

else

printf("Dorsal no encontrado");

}

/****************************************************************************

void listar(void)

Función que presenta en pantalla la lista de participantes.

****************************************************************************/ void listar(void)

{

register int n;

printf("\n\nListado de participantes introducidos\n\n");

for (n=0; n<MAX; n++)

if (participantes[n].nombre[0]) {

printf("%s ", participantes[n].nombre);

printf("%s / ", participantes[n].apellido);

printf("%d /", participantes[n].dorsal);

printf("%d\n", participantes[n].edad);

}

}

Page 37: ESTRUCTURAS ESTATICAS

Estructuras anidadas

Cuando una estructura forma parte de otra decimos que estamos ante una

estructura anidada.

Para ilustrar esta idea podemos completar el ejemplo anterior. Ahora, queremos

almacenar también la dirección de cada competidor. La estructura anidada sería

entonces:

struct domicilio {

unsigned char calle[30];

int num;

unsigned char localidad[25];

};

struct competidor {

unsigned char nombre[20];

unsigned char apellidos[50];

int dorsal;

int edad;

struct domicilio dirección;

};

struct competidor participantes[20];

Para presentar en pantalla, por ejemplo, la calle del quinto participante

deberíamos escribir:

puts(participantes[4].direccion.calle);

Paso de estructuras como argumentos

Ya comentamos que otra de las ventajas más evidentes de las estructuras es la

de poder utilizarlas como modo de enviar un argumento complejo a una función.

Page 38: ESTRUCTURAS ESTATICAS

Recordamos que los argumentos pueden ser pasados por valor o por dirección. En

el primer caso la función crea una copia interna y temporal del argumento para

trabajar con él, copia que desaparece cuando la función acaba. En el segundo caso

lo que se pasa a la función como argumento es un puntero a la variable para que

sepa donde encontrarla dentro de la memoria y trabajar directamente con ella.

Cuando pasamos una estructura de tamaño importante habrá que tener en cuenta

la sobrecarga que supone introducir en la pila de ejecución de la función una copia

completa de todo el conjunto de datos. Normalmente será más conveniente pasar

las estructuras por dirección para conseguir una ejecución más rápida y, por

tanto, más eficiente.

Para hacerlo necesitamos definir punteros a estructuras de igual forma a como

hacíamos con otros tipo de datos.

Modifiquemos el ejemplo anterior para que la función listar no presente en

pantalla directamente el array de estructuras sino que haga una llamada a una

función imprimir() que es la que se encarga de esa labor. Para pasarle la

información a presentar utilizamos un puntero a la estructura competidor. Con

esto evitamos la sobrecarga que supondría para la pila del programa el paso de la

estructura completa.

Para acceder a los miembros de una estructura a través de un puntero

deberemos utilizar un nuevo operador —comúnmente llamado flecha y compuesto

de un guión y el símbolo de mayor: ->— en lugar del operador punto.

El programa quedaría entonces como sigue:

/* Mostrar la lista de participantes */

void listar(void)

{

int n;

Page 39: ESTRUCTURAS ESTATICAS

void imprimir(struct competidor *c);

for (n=0; n<MAX; n++)

imprimir(&participantes[nj);

}

void imprimir(struct competidor *c)

{

if(c->nombre[0]) /* equivalente a strcmp(c->nombre, "") */

{

printf("%s ", c->nombre);

printf("%s /", c->apellido);

printf("%d /", c->dorsal);

printf("%d\n", c->edad);

}

}

5.5.2 Enumeraciones

Una enumeración en lenguaje C no es más que una agrupación, bajo un nombre

común, de un conjunto de constantes, en el que se incluyen todos los valores

posibles para una variable que se declare de ese nuevo tipo. Las enumeraciones

son habituales en la mayoría de problemas y su utilización podrá ayudar en gran

medida a hacer más legible el código fuente.

La forma general de utilizar las enumeraciones es muy semejante a la de las

estructuras. El nombre de una enumeración es opcional y su declaración no

reserva espacio de memoria hasta que existan variables que se declaren de ese

tipo.

enum dias {lunes, martes, miércoles, jueves, viernes};

enum dias hoy;

declaración que, como siempre, podrá ser resumida en una única instrucción:

enum dias {lunes, martes, miércoles, jueves, viernes} hoy;

Page 40: ESTRUCTURAS ESTATICAS

A partir de este momento la variable hoy tiene su espacio reservado en memoria

y podrá tomar valores de entre los enumerados. Así, serían válidas sentencias

como:

hoy = jueves;

if (hoy = = viernes) printf ("La hora de salida se adelanta a las 2\n");

Internamente el compilador asigna un número entero a cada uno de los elementos

de la enumeración, empezando en O e incrementando en 1 para cada valor. Es por

eso que esos símbolos se pueden usar en cualquier expresión entera.

for (n=lunes; n<=viernes; n++) printf("%d", n);

Vemos en el siguiente programa cómo la utilización de una enumeración podrá

hacerse indistintamente utilizando sus propias etiquetas o los números enteros

que las representan.

294.

/****************************************************************************

enum.c

Ejemplo de utilización de una enumeración que presenta la duración de las distintas

jornadas laborales.

****************************************************************************/ #include <stdio.h>

void main(void)

{

enum dias {lunes, martes, miercoles, jueves, viernes, sabado, domingo};

enum dias hoy;

unsigned char texto_dias[ ][10]={"lunes", "martes", "miércoles", "jueves", "viernes", "sabado", "domingo"};

printf("\n\nPROGRAMA QUE UTILIZA LA ENUMERACIÓN\n\n");

printf("Introduzca el número de un día de la semana (1-7): ");

Page 41: ESTRUCTURAS ESTATICAS

do

scanf("%d", &hoy);

while (hoy<1 || hoy>7);

hoy--;

printf("La jornada laboral del %s es de ", texto_dias[hoy]);

switch(hoy) {

case lunes:

case martes:

case miercoles:

case 3:

case viernes:

printf("8 horas\n");

break;

case sabado:

printf("5 horas\n");

break;

case 6:

printf("0 horas\n");

break;

}

}

Se resalta así que no hay que confundir las etiquetas de una enumeración con

textos imprimibles. Esas etiquetas no son más que símbolos que el compilador

sustituirá por sus correspondientes valores enteros. Su utilidad estará, por

tanto, en que incrementan la legibilidad de los programas y permiten hacer una

escritura más significativa, objetivos nada desdeñables.

Page 42: ESTRUCTURAS ESTATICAS

5.5.3 Uniones

En lenguaje C, definimos una unión como una posición de memoria que es

compartida por más de una variable, normalmente de distinto tipo, en diferentes

momentos.

El compilador reservará el espacio necesario para contener al mayor de los tipos

que forman la unión.

Igual que en los casos anteriores, se distingue entre la declaración de la unión y

la declaración de una variable de ese tipo:

unión tipo_union

{

int i;

char c;

}

unión tipo_union ent_y_char;

Hemos declarado una variable ent_y_char para la que el compilador reservará los

bytes que ocuparía un número entero, por ser mayor que un char.

Para acceder a un miembro de una unión, la sintaxis es idéntica a la utilizada para

las estructuras, se usan los operadores punto y flecha. El primero se utiliza

cuando se trabaja directamente con la unión y el segundo cuando se hace con un

puntero a ella.

Por ejemplo, para asignar directamente un valor entero a nuestra variable

haríamos:

ent_y_char.i = 10;

Pero si estuviésemos trabajando con un puntero, probablemente pasado como

argumento a una función, tendríamos:

Page 43: ESTRUCTURAS ESTATICAS

void f(union tipo_union *p)

{

p->i=10;

}

El uso de las uniones podrá ayudar a los programadores, fundamentalmente, a

crear código independiente de la máquina, lo más portable posible. Será el

compilador el que se encargue de asignar el tamaño necesario para cada variable.

Las uniones son útiles para interpretar de diferentes modos una representación

binaria y se utilizan en ocasiones en las que se necesitan conversiones complejas

de tipos

5.5.4 Campos de bits

C se distingue de muchos otros lenguajes de programación por incorporar este

cómodo y elegante método para acceder a bits individuales. Su aplicación puede

ser necesaria en muy diferentes ocasiones: para almacenar en un solo byte varias

variables que puedan tomar valor verdadero o falso, para trabajar con

dispositivos que emiten o reciben información en bits o, por ejemplo, para algunas

rutinas de cifrado que trabajan directamente con bits.

Con los campos de bits estas operaciones se podrán realizar con eficiencia al

mismo tiempo que se genera un código fuente claro y estructurado. Para realizar

las mismas operaciones podrían utilizarse los operadores de bits, pero

obtendríamos un resultado mucho menos claro.

En realidad, los campos de bits no son más que un caso particular de las

estructuras en los que se declara simplemente la longitud en bits de cada

elemento particular.

Page 44: ESTRUCTURAS ESTATICAS

Imaginemos que queremos controlar directamente desde nuestro programa el

funcionamiento de un modem y que debemos comunicarnos con él utilizando bits

individuales. Podríamos declarar un campo de bits parecido al siguiente:

struct tipo estado

{

unsigned cambio_linea: 1;

unsigned cambio_datos: 1;

unsigned final: 1;

unsigned cambio_rec: 1;

unsigned listo: 1;

unsigned datos: 1;

unsigned llamada: 1;

unsigned linea: 1;

} estado;

Si suponemos que existe una función capaz de preguntar por el estado del

dispositivo y almacenarlo en la variable estado^ después podremos utilizar

cómodamente cada uno de sus bits individuales en nuestro programa.

estado = obtener_estado();

if (estado.listo) printf("Listo para enviar\nff);

if (estado.linea) printf("Detectada línea\n");

Los campos de bits se utilizan en ocasiones mezclados como miembros de

estructuras normales:

struct competidor

{

unsigned char nombre[20];

unsigned char apellidos[50];

int dorsal;

int edad;

unsigned federado: 1;

unsigned sexo: 1

unsigned prueba: 6;

};

Page 45: ESTRUCTURAS ESTATICAS

En este ejemplo se utiliza un único byte para almacenar tres elementos de

información, si el competidor está federado o no, su sexo y el tipo de prueba en

que participa. De otra forma, esa misma información ocuparía probablemente

tres o cuatro bytes.

5.5.5 Typedef

Esta palabra reservada de C en realidad no sirve para crear un nuevo tipo de

datos, sino para darle un nuevo nombre a un tipo ya existente.

La utilidad del uso de typedef está, por una parte, en que puede colaborar a la

portabilidad de los programas, pensando en que puedan ser ejecutados en

máquinas con diferencias de tamaño en los tipos básicos. SÍ, por ejemplo,

definimos:

typedef int numero;

a partir de este momento podremos utilizar la palabra numero para declarar

variables de tipo int. Si en algún momento necesitamos otro tipo para todas esas

variables, por ser dependientes de la máquina, sólo precisaríamos cambiar esta

sentencia, sin necesidad de buscar en el programa los lugares donde sea

necesario realizar el cambio.

Por otra parte, typedef puede colaborar claramente en hacer más legibles los

programas al permitir al programador dar nombres más descriptivos a los tipos

de datos.

numero i, valor, x;

Otra aplicación útil de esta sentencia es la de simplificar y hacer más legibles las

declaraciones de variables de estructura, de unión o de enumeración como las que

hemos visto anteriormente.

Page 46: ESTRUCTURAS ESTATICAS

typedef struct tipo_competidor

{

unsigned char nombre[20];

unsigned char apellidos[50];

int dorsal;

int edad;

} competidor;

Ahora, en lugar de:

struct tipo_competidor participan tes[20j;

podríamos utilizar una sentencia más legible:

competidor participantes [20];

5.6 EJERCICIOS

285.

/****************************************************************************

apuest.c

Visualiza varias apuestas de la loter¡a primitiva en la pantalla.

Cada apuesta de loter¡a primitiva son seis n£meros diferentes comprendidos

entre 1 y 49.

El numero de apuestas se leer desde el teclado.

****************************************************************************/

#include <stdio.h>

#include <time.h>

#include <stdlib.h>

void apuesta(void);

void main(void)

{

Page 47: ESTRUCTURAS ESTATICAS

int i, na;

printf("\n\nAPUESTAS DE LOTERIA PRIMITIVA\n\n");

srand(time(NULL));

printf("\n N§ de apuestas: ");

scanf("%d",&na);

for(i=1;i<=na;i++)

apuesta();

}

/****************************************************************************

void apuesta(void)

Funci¢n que presenta en pantalla cada una de las apuestas aleatorias.

****************************************************************************/

void apuesta(void)

{

int i=0,n,lot[49]={0};

while(i<6)

{

n=rand()%49;

if(!lot[n])

{

lot[n]=1;

i++;

}

}

printf("\n");

Page 48: ESTRUCTURAS ESTATICAS

for(i=0;i<49;i++)

if(lot[i])

printf(" %2d ",i+1);

}

286.

/**********************************************************************

apuest2.c

Visualiza varias apuestas de la loter¡a primitiva en la pantalla.

En este caso las funciones reciben un puntero a un array de numeros

enteros como argumento.

**********************************************************************/

#include <stdio.h>

#include <time.h>

#include <stdlib.h>

void apuesta(int lot[ ]);

void visapuesta(int lot[ ]);

void main(void)

{

int i,na,lot[6];

printf("\n\nAPUESTAS DE LOTERIA PRIMITIVA\n\n");

srand(time(NULL));

printf("\n N§ de apuestas: ");

scanf("%d",&na);

for(i=1; i<=na; i++){

apuesta(lot);

Page 49: ESTRUCTURAS ESTATICAS

visapuesta(lot);

}

}

/**********************************************************************

void apuesta(int lot[ ])

Funci¢n que llena el array con los n£meros de cada una de

las apuestas aleatorias.

**********************************************************************/

void apuesta(int lot[ ])

{

int i=0,j,n,enc;

while(i<6){

n=rand()%49+1; /* genera numero entre 1 y 49 */

enc=0;

j=0;

while(j<i && !enc)

if(lot[j]==n)

enc=!enc;

else

j++;

if(!enc){ /* si no salir el numero */

lot[j]=n;

i++;

}

}

}

Page 50: ESTRUCTURAS ESTATICAS

/**********************************************************************

void visapuesta(int lot[ ])

Funci¢n que presenta en pantalla el array de cada una de las apuestas.

**********************************************************************/

void visapuesta(int lot[ ])

{

int i;

printf("\n");

for(i=0;i<6;i++)

printf(" %2d ",lot[i]);

}

287.

/****************************************************************************

cuentac.c

Programa que cuenta las apariciones de un carácter dentro de una cadena de

caracteres, leídos ambos desde la entrada estándar.

****************************************************************************/

#include <stdio.h>

#include <string.h>

#define MAX 50

void main(void)

{

unsigned char cadena[MAX], caracter;

int i, veces=0;

printf("\n\nCUENTA DE CARACTERES DENTRO DE UNA FRASE\n\n");

Page 51: ESTRUCTURAS ESTATICAS

printf("Introduzca una frase (de menos de %d caracteres): ",MAX);

gets(cadena);

printf("Introduzca el carácter a contar: ");

scanf("%c", &caracter);

for (i=0; i<strlen(cadena)&&i<MAX; i++)

if(cadena[i]==caracter) veces++;

printf("El carácter %c aparece %d veces en la cadena.\n", caracter, veces);

}

288.

/****************************************************************************

media.c

Programa que calcula el valor medio de un conjunto de valores enteros.

****************************************************************************/

#include <stdio.h>

#define MAX 10

void main(void)

{

int i, nvalores, valores [MAX];

float suma = 0;

printf("\n\nVALOR MEDIO DE UN MAXIMO DE %d VALORES ENTEROS\n\n", MAX);

printf("N£mero de valores a introducir (mayor que 0): ");

scanf("%d", &nvalores);

Page 52: ESTRUCTURAS ESTATICAS

if (nvalores<1)

nvalores=1;

if (nvalores>MAX)

nvalores=MAX;

for (i=0; i<nvalores; i++) {

printf("valor %d: ", i+1);

scanf("%d", &valores[i]);

}

printf("Los valores introducidos son: ");

for (i=0; i<nvalores; i++) {

printf(" %d ", valores[i]);

suma += valores[i];

}

printf("\ny la media resulta: %.2f\n", suma/nvalores);

}

289.

/****************************************************************************

sobremed.c

Programa que presenta los valores de un conjunto de enteros que estan

por encima de la media.

****************************************************************************/

#include <stdio.h>

#define MAX 10

void main(void)

Page 53: ESTRUCTURAS ESTATICAS

{

int i, nvalores, valores [MAX];

float suma=0, media;

printf("\n\nVALORES POR ENCIMA DE LA MEDIA DE UN MAXIMO DE %d VALORES

ENTEROS\n\n", MAX);

printf("N£mero de valores a introducir (mayor que 0): ");

scanf("%d", &nvalores);

if (nvalores<1)

nvalores=1;

if (nvalores>MAX)

nvalores=MAX;

for (i=0; i<nvalores; i++) {

printf("valor %d: ", i+1);

scanf("%d", &valores[i]);

suma += valores[i];

}

media = suma / nvalores;

printf("Los valores por encima de %.2f son: \n", media);

for (i=0; i<nvalores; i++)

if (valores[i]>media)

printf(" n§ %d: %d\n", i+1, valores[i]);

}