• No results found

Template analysis: from categorizing to interpreting data

3.5 Data analysis

3.5.1 Template analysis: from categorizing to interpreting data

Una característica algo confusa pero muy útil de C es el puntero a función. La confusión surge porque una función tiene una posición física en memoria que puede asignarse a un puntero aunque la función no es una variable. La dirección de la función es el punto de entrada de la función; por tanto, un puntero a función puede utilizarse para llamar a la función.

Ejemplo:

~#include <stdio.h> /* printf () */ ~

~void main (void) ~{

~ /* escribir es una función que acepta un int como argumento ~ y no devuelve nada */

~ void escribir (int); ~

~ /* pf es un puntero a una función que acepta un int como argumento ~ y no devuelve nada */

~ void (*pf) (int); ~

~ escribir (1); /* llama a la función escribir */

~ (*pf) (2); /* llama a la función escribir a través de un puntero */ ~}

~

~void escribir (int numero) ~{

~ printf ("%d", numero); ~}

La salida de este programa por pantalla es: 12

Una utilidad de los punteros a funciones la tenemos en las funciones qsort() y bsearch() cuyos prototipos están en el fichero stdlib.h.

qsort ()

El prototipo de la función qsort() está en el fichero stdlib.h y es: void qsort (void *base, unsigned int num, unsigned int tam,

int (*compara) (void *arg1, void *arg2)); La función qsort() ordena el array apuntado por base utilizando el método de ordenación de C.A.R. Hoare (este método se ha explicado en el ejemplo 3 de la lección 5). El número de elementos en el array se especifica mediante num, y el tamaño en bytes de cada elemento está descrito por tam.

La función compara se utiliza para comparar un elemento del array con la clave. La comparación debe ser:

int nombre_func (void *arg1, void *arg2); Debe devolver los siguientes valores:

Si arg1 es menor que arg2, devuelve un valor menor que 0. Si arg1 es igual a arg2 devuelve 0.

Si arg1 es mayor que arg2, devuelve un valor mayor que 0.

El array es ordenado en orden ascendente con la dirección más pequeña conteniendo el menor elemento.

En Turbo C, el prototipo de la función qsort() es ligeramente diferente: void qsort (void *base, size_t num, size_t tam,

int (*compara) (const void *, const void *));

size_t es un tipo definido en el fichero stdlib.h y suele ser unsigned int; (const void *) no es lo mismo que (void *), la diferencia entre ellos se va a estudiar unas tres o cuatro ventanas más adelante, pero podemos intuirla: en (const void *) el objeto apuntado es constante, es decir, no se puede modificar, en (void *) el objeto apuntado por el puntero sí se puede modi- ficar.

Veamos un ejemplo de la utilización de esta función, donde podemos apreciar además, dos formas posibles de declaración y utilización de la función de comparación requerida por la función qsort().

~

~void main (void) ~{

~ int num[10] = { 3, 2, 8, 9, 2, 2, 1, -2, 3, 2 }; ~ register int i;

~ int comparar_creciente (const void *elem1, const void *elem2); ~ int comparar_decreciente (const int *elem1, const int *elem2); ~

~ printf ("\nArray desordenado: "); ~ for (i = 0; i < 10; i++)

~ printf ("%d ", num[i]); ~

~ qsort (num, 10, sizeof (int), comparar_creciente); ~

~ printf ("\nArray ordenado en orden creciente: "); ~ for (i = 0; i < 10; i++)

~ printf ("%d ", num[i]); ~

~ /*

~ el molde del cuarto argumento convierte el tipo ~ (int (*) (const int *, const int *))

~ al tipo

~ (int (*) (const void *, const void *)) ~ que es el que requiere la función qsort ~ */

~ qsort (num, 10, sizeof (int),

~ (int (*) (const void *, const void *)) comparar_decreciente); ~

~ printf ("\nArray ordenado en orden decreciente: "); ~ for (i = 0; i < 10; i++)

~ printf ("%d ", num[i]); ~}

~

~int comparar_creciente (const void *elem1, const void *elem2) ~{

~ /* para acceder al contenido de un puntero del tipo (void *) ~ necesitamos moldearlo a un tipo base que no sea void */ ~ return (*(int *)elem1 - *(int *)elem2);

~} ~

~int comparar_decreciente (const int *elem1, const int *elem2) ~{

~ return (*elem2 - *elem1); ~}

La salida de este programa por pantalla es: Array desordenado: 3 2 8 9 2 2 1 -2 3 2

Array ordenado en orden creciente: -2 1 2 2 2 2 3 3 8 9 Array ordenado en orden decreciente: 9 8 3 3 2 2 2 2 1 -2

bsearch ()

El prototipo de la función bsearch() se encuentra en el fichero stdlib.h y es el siguiente:

void *bsearch (void *clave, void *base, unsigned int num, unsigned int tam, int (*compara) (void *arg1, void *arg2));

La función bsearch() realiza una búsqueda binaria en el array ordenado apuntado por base y devuelve un puntero al primer elemento que se corres- ponde con la clave apuntada por clave. El número de elementos en el array está especificado por num y el tamaño (en bytes) de cada elemento está descrito por tam.

La función apuntada por compara se utiliza para comparar un elemento del array con la clave. La forma de la función de comparación debe ser: nombre_func (void *arg1, void *arg2);

Debe devolver los siguientes valores:

Si arg1 es menor que arg2, devuelve un valor menor que 0. Si arg1 es igual que arg2, devuelve 0.

Si arg1 es mayor que arg2, devuelve un valor mayor que 0.

El array debe estar ordenado en orden ascendente con la menor dirección conteniendo el elemento más pequeño. Si el array no contiene la clave, se devuelve un puntero nulo.

Esta función está implementada en uno de los ejemplos de la lección 3. En Turbo C, el prototipo de la función bsearch() es ligeramente diferente: void *bsearch (const void *clave, const void *base, unsigned int *num, unsigned int tam, int (*compara) (const void *arg1, const void *arg2)); Los tipos size_t y (const void *) se han explicado en la ventana anterior: qsort().

Ejemplo:

~#include <stdlib.h> ~#include <stdio.h> ~

~#define NUM_ELEMENTOS(array) (sizeof(array) / sizeof(array[0])) ~

~int array_de_numeros[] = { 123, 145, 512, 627, 800, 933, 333, 1000 }; ~

~int comparacion_de_numeros (const int *p1, const int *p2) ~{

~ return (*p1 - *p2); ~}

~

~int buscar (int clave) ~{

~ int *puntero_a_elemento; ~

~ /* El molde (int (*) (const void *, const void *)) es necesario para ~ evitar un error de tipo distinto en tiempo de compilación. Sin ~ embargo, no es necesario: puntero_a_elemento = (int *) bsearch (... ~ debido a que en este caso es el compilador el que realiza la

~ conversión de tipos */

~ puntero_a_elemento = bsearch (&clave, array_de_numeros, ~ NUM_ELEMENTOS (array_de_numeros), sizeof (int),

~ (int (*) (const void *, const void *)) comparacion_de_numeros); ~

~ return (puntero_a_elemento != NULL); ~}

~ if (buscar (800))

~ printf ("800 está en la tabla.\n"); ~ else

~ printf ("800 no está en la tabla.\n"); ~

~ return 0; ~}

La salida de este programa por pantalla es: 800 está en la tabla.

lfind () y lsearch ()

Estas dos funciones no pertenecen al ANSI C actual pero sí están incluidas en las versiones de Turbo C.

Ambas funciones realizan una búsqueda lineal.

Sus prototipos están en el fichero stdlib.h y son los siguientes:

void *lfind (const void *clave, const void *base, size_t *num, size_t anchura, int (*func_de_comp) (const void *elem1, const void *elem2)); void *lsearch (const void *clave, void *base, size_t *num, size_t anchura, int (*func_de_comp) (const void *elem1, const void *elem2)); Estas funciones utilizan una rutina definida por el usuario (func_de_comp) para la búsqueda de la clave, en un array de elementos secuenciales.

El array tiene num elementos, cada uno de tamaño anchura bytes y comienza en la dirección de memoria apuntada por base.

Devuelve la dirección de la primera entrada en la tabla que coincida con la clave buscada. Si la clave buscada no se encuentra, lsearch la añade a la lista; lfind devuelve 0.

La rutina *func_de_comp debe devolver cero si *elem1 == *elem2, y un valor distinto de cero en caso contrario.

Ejemplo de la función lfind: ~#include <stdio.h>

~#include <stdlib.h> ~

~int comparar (int *x, int *y) ~{

~ return (*x - *y); ~}

~

~int main (void) ~{ ~ int array[5] = { 5, -1, 100, 99, 10 }; ~ size_t nelem = 5; ~ int clave; ~ int *resultado; ~

~ clave = 99;

~ resultado = lfind(&clave, array, ≠lem, sizeof (int),

~ (int (*) (const void *, const void *)) comparar); ~ if (resultado)

~ printf ("Número %d encontrado\n", clave); ~ else

~ printf ("Número %d no encontrado.\n", clave); ~

~ return 0; ~}

La salida de este programa es: Número 99 encontrado.

Ejemplo de la función lsearch: ~#include <stdlib.h> ~#include <stdio.h> ~ ~int numeros[10] = { 3, 5, 1 }; ~int nnumeros = 3; ~

~int comparar_numeros (int *num1, int *num2) ~{

~ return (*num1 - *num2); ~}

~

~int aniadir_elemento (int numero_clave) ~{

~ int viejo_nnumeros = nnumeros; ~

~ lsearch ((void *) νmero_clave, numeros, ~ (size_t *) &nnumeros, sizeof (int),

~ (int (*) (const void *, const void *)) comparar_numeros); ~

~ return (nnumeros == viejo_nnumeros); ~}

~

~int main (void) ~{

~ register int i; ~ int clave = 2; ~

~ if (aniadir_elemento (clave))

~ printf ("%d está ya en la tabla de números.\n", clave); ~ else

~ printf ("%d está añadido a la tabla de números.\n", clave); ~

~ printf ("Números en tabla:\n"); ~ for (i = 0; i < nnumeros; i++) ~ printf ("%d\n", numeros[i]); ~

~ return 0; ~}

La salida de este programa es:

2 está añadido a la tabla de números. Números en tabla