Вы находитесь на странице: 1из 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 mu y

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


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
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:


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.


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:
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:


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]);
}
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);

}
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);
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);
}
/**********************************************************************
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
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.
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.

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.

/**********************************************************************
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);
if(gets(cadena)!=NULL)
for (i=0; cadena[i]; i++)
printf("%c", cadena[i]);
}

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.

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);
}

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.

264.
/****************************************************************************
sprintf.c
Da formato a un texto que se almacena en una cadena de caracteres.
****************************************************************************/
#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);

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.

265.
/****************************************************************************
strlen.c
Presenta en pantalla la longitud de una cadena de caracteres tecleada.
****************************************************************************/
#include <stdio.h>
#include <string.h>

#define MAX 50
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);

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.

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
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);
}

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 .

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];


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.

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.

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;


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");

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.

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 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 .
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.
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);

}
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


Estas son algunas
caracteres denúmero
s en un las funciones que proporciona
de tipo unsigned long. 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.


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).

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;
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
{

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.

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.

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.
****************************************************************************/
#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)

{
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.
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.


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;
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.
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);
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;
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 {
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: ");


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);

}
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.


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;
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;


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): ");


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.


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:


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.
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;
};
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.


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)

{
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");
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);
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++;

}
/**********************************************************************
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");


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);
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)
{

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]);

Вам также может понравиться