funciones2
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Próxima revisión | Revisión previa | ||
| funciones2 [2013/04/12 20:00] – creado lmateu | funciones2 [2014/09/24 21:36] (actual) – [Ejercicios] lmateu | ||
|---|---|---|---|
| Línea 48: | Línea 48: | ||
| === Primer ejemplo de uso === | === Primer ejemplo de uso === | ||
| - | El siguiente programa utiliza la función anterior para ordenar líneas lexicográficamente: | + | El siguiente programa utiliza la función anterior para ordenar líneas lexicográficamente |
| < | < | ||
| - | #include < | + | % ordenar juan pedro diego |
| - | # | + | diego |
| + | juan | ||
| + | pedro | ||
| + | </code> | ||
| - | void qsort(void *a[], int left, int right, | + | Esta es la solución usando |
| - | int (*compare)(void *, void *)); | + | |
| - | + | ||
| - | #define ANCHO 1000 | + | |
| - | #define MAX 10000 | + | |
| - | | + | < |
| - | char s[ANCHO]; | + | |
| - | char *linea[MAX]; | + | char *s1= (char*)p1; |
| - | | + | char *s2= (char*)p2; |
| - | /* El siguiente es el criterio para las comparaciones */ | + | |
| - | int (*compare)(void *, void *)= (int (*)(void *, void*)) strcmp; /* Ver nota */ | + | } |
| - | + | ||
| - | | + | |
| - | linea[i]= strdup(s); | + | |
| - | qsort((void **)linea, 0, i-1, compare); | + | qsort((void**)argv, 1, argc-1, mistrcmp); |
| - | for (j= 0; j<i; ++j) | + | for (j= 1; j<argc; ++j) |
| - | | + | |
| } | } | ||
| + | </ | ||
| + | |||
| + | === Segundo ejemplo de uso === | ||
| + | |||
| + | Esto no es elegante pero permite usar directamente la función strcmp, sin tener que definir mistrcmp. | ||
| + | |||
| + | < | ||
| + | int (*compare)(void *, void *)= (int (*)(void *, void*)) strcmp; /* Ver nota */ | ||
| + | qsort((void**)argv, | ||
| </ | </ | ||
| Línea 80: | Línea 88: | ||
| para compatibilizar strcmp con el tipo de la variable asignada. | para compatibilizar strcmp con el tipo de la variable asignada. | ||
| Si se pasa directamente strcmp a qsort, el compilador podría reclamar conflicto | Si se pasa directamente strcmp a qsort, el compilador podría reclamar conflicto | ||
| - | de tipos. | + | de tipos. |
| + | de la función son punteros y sabemos que un cast entre punteros no realiza | ||
| + | ninguna conversión. | ||
| + | no son punteros. | ||
| + | impredescibles: | ||
| - | Otra forma de hacer esto mismo: | + | < |
| + | int h(int x, double y); | ||
| + | int (*f)(double x, int k)= (int (*)(double x, int k)) h; | ||
| + | </ | ||
| + | |||
| + | Si no se coloca el cast, el compilador reclama porque no hay concordancia de tipos. | ||
| + | Con el cast, el compilador no reclama, pero al invocar f mediante: | ||
| + | |||
| + | < | ||
| + | int n= (*f)(3.14, 1); | ||
| + | int m= (*f)(3, 1.5); | ||
| + | </ | ||
| + | |||
| + | En ningún caso la función h recibirá algo parecido a los argumentos usados en estas invocaciones. | ||
| + | El error ocurre porque en este caso los argumentos de la función sí requieren conversión de tipo | ||
| + | (entero a real o viceversa), pero debido al cast de funciones el compilador no realizará | ||
| + | ninguna conversión. | ||
| + | |||
| + | Otra forma de lograr lo mismo, aunque no es más o menos eficiente, es: | ||
| < | < | ||
| Línea 98: | Línea 128: | ||
| </ | </ | ||
| - | Lo cual es mucho más legible. | + | Lo cual es más legible. |
| - | === Segundo | + | === Tercer |
| + | |||
| + | Este ejemplo ordena los strings por valor numérico: | ||
| < | < | ||
| - | int numcmp(char *s1, char *s2) /* compara numéricamente */ { | + | int numcmp(void *s1, void *s2) /* compara numéricamente */ { |
| int i1, i2; | int i1, i2; | ||
| - | |||
| - | i1= atoi(s1); | ||
| - | i2= atoi(s2); | ||
| - | |||
| - | return i1<i2? -1 : i1==i2? 0 : 1; | ||
| - | } | ||
| - | main() { | + | i1= atoi((char*)s1); |
| - | char s[ANCHO]; | + | |
| - | | + | |
| - | | + | |
| - | Comparator compare= (Comparator)numcmp; | + | |
| - | | + | |
| - | | + | } |
| + | |||
| + | int main(int argc, char **argv) { | ||
| + | int j; | ||
| - | qsort((void **)linea, 0, i-1, compare); | + | qsort((void**)argv, 1, argc-1, numcmp); |
| - | for (j= 0; j<i; ++j) | + | for (j= 1; j<argc; ++j) |
| - | | + | |
| } | } | ||
| </ | </ | ||
| Línea 131: | Línea 157: | ||
| un programa que recibe la opción " | un programa que recibe la opción " | ||
| por omisión ordena alfabéticamente. | por omisión ordena alfabéticamente. | ||
| + | |||
| + | ==== Ordenar un arreglo de enteros ==== | ||
| + | |||
| + | ¿Cómo se podría usar qsort para ordenar un arreglo de enteros? | ||
| + | |||
| + | < | ||
| + | int a[6]= {3, 7, 8, 10, 1, 2}; | ||
| + | </ | ||
| + | |||
| + | Hay 2 formas: la elegante y la abreviada. | ||
| + | podemos usar directamente el arreglo de enteros porque qsort recibe un arreglo de punteros, | ||
| + | no enteros. | ||
| + | |||
| + | < | ||
| + | int *ap[6]= { &a[0], &a[1], &a[2], &a[3], &a[4], &a[5] }; | ||
| + | </ | ||
| + | |||
| + | Y ahora programamos la función de comparación y el main: | ||
| + | |||
| + | < | ||
| + | int intcmp(void *p1, void *p2) { | ||
| + | int i1= *(int*)p1; | ||
| + | int i2= *(int*)p2; | ||
| + | if (i1<i2) | ||
| + | return -1; | ||
| + | else if (i1==i2) | ||
| + | return 0; | ||
| + | else | ||
| + | return 1; | ||
| + | } | ||
| + | | ||
| + | int main() { | ||
| + | int i; | ||
| + | qsort((void**)ap, | ||
| + | for (i= 0; i<6; i++) | ||
| + | printf(" | ||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | El cast '' | ||
| + | |||
| + | === Forma abreviada === | ||
| + | |||
| + | La forma abreviada de ordenar un arreglo de enteros es hacerle creer a qsort que los enteros son punteros. | ||
| + | |||
| + | < | ||
| + | /* Este programa solo funciona en plataformas de 32 bits, no cuando son de 64 bits */ | ||
| + | int a[6]= {3, 7, 8, 10, 1, 2}; | ||
| + | | ||
| + | int intcmp(void *p1, void *p2) { | ||
| + | int i1= (int)p1; /* ¡la direccion almacenada es el entero! */ | ||
| + | int i2= (int)p2; | ||
| + | if (i1<i2) | ||
| + | return -1; | ||
| + | else if (i1==i2) | ||
| + | return 0; | ||
| + | else | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | int main() { | ||
| + | int i; | ||
| + | qsort((void**)ap, | ||
| + | for (i= 0; i<6; i++) | ||
| + | printf(" | ||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Este truco solo funciona en arquitecturas en donde int sea del mismo tamaño que un puntero, i.e. x86 de 32 bits. | ||
| + | No funcionará en amd64 por ejemplo. | ||
| + | correctamente en x86, al compilarlos en una arquitectura de 64 bits, ya no funcionan. | ||
| + | |||
| + | === Forma portable === | ||
| + | |||
| + | La siguiente solución debería funcionar en todas las plataformas. | ||
| + | los enteros como punteros en el arreglo que recibe qsort: | ||
| + | |||
| + | < | ||
| + | void *a[6]= {(void*)3, (void*)7, (void*)8, (void*)10, (void*)1, (void*)2}; | ||
| + | | ||
| + | int longcmp(void *p1, void *p2) { | ||
| + | long i1= (long)p1; /* la direccion almacenada es el entero! */ | ||
| + | long i2= (long)p2; | ||
| + | if (i1<i2) | ||
| + | return -1; | ||
| + | else if (i1==i2) | ||
| + | return 0; | ||
| + | else | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | int main() { | ||
| + | int i; | ||
| + | qsort(a, 0, 5, longcmp); | ||
| + | for (i= 0; i<6; i++) | ||
| + | printf(" | ||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | En Windows de 64 bits el tipo long es de 32 bits como | ||
| + | se explica [[http:// | ||
| + | y por lo tanto no coincide con el tamaño de un puntero. | ||
| + | entregue algún warning. | ||
| + | |||
| + | Enfín, el autor de este documento no recomienda escribir este tipo de código, pero sí entenderlo | ||
| + | porque hay abundante código del legado que recurre a este tipo de trucos y por lo tanto es importante | ||
| + | ser capaz de modificarlo si se requieren cambios. | ||
| + | |||
| + | ==== Ejercicios ==== | ||
| + | |||
| + | * Resuelva la pregunta 3 parte i.- del [[http:// | ||
| + | * Resuelva la pregunta 1 del [[http:// | ||
| + | |||
funciones2.1365796830.txt.gz · Última modificación: por lmateu
