Herramientas de usuario

Herramientas del sitio


funciones2

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
funciones2 [2013/04/12 20:22] lmateufunciones2 [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 los parámetros recibos desde la línea de comandos.  Por ejemplo:
  
 <code> <code>
-  #include <stdio.h> +% ordenar juan pedro diego 
-  #include <string.h>+diego 
 +juan 
 +pedro 
 +</code>
  
-  void qsort(void *a[], int left, int right+Esta es la solución usando qsort
-             int (*compare)(void *, void *)); + 
-             +<code> 
 +  int mistrcmp(void *p1void *p2) { 
 +    char *s1= (char*)p1; 
 +    char *s2= (char*)p2; 
 +    return strcmp(s1s2); 
 +  } 
 +               
   int main(int argc, char **argv) {   int main(int argc, char **argv) {
     int j;     int j;
-    int (*compare)(void *, void *)= (int (*)(void *, void*)) strcmp; /* Ver nota */ 
  
-    qsort((void**)argv, 1, argc-1, compare);+    qsort((void**)argv, 1, argc-1, mistrcmp);
  
     for (j= 1; j<argc; ++j)     for (j= 1; j<argc; ++j)
       printf("%s\n", argv[j]);       printf("%s\n", argv[j]);
   }   }
 +</code>
 +
 +=== Segundo ejemplo de uso ===
 +
 +Esto no es elegante pero permite usar directamente la función strcmp, sin tener que definir mistrcmp.
 +
 +<code>
 +  int (*compare)(void *, void *)= (int (*)(void *, void*)) strcmp; /* Ver nota */
 +  qsort((void**)argv, 1, argc-1, compare);
 </code> </code>
  
Línea 71: 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.  Este cast es correcto en este caso porque todos los parámetros 
 +de la función son punteros y sabemos que un cast entre punteros no realiza 
 +ninguna conversión.  ¡Pero cuidado!  Los efectos son impredecibles si los parámetros 
 +no son punteros.  Por ejemplo el siguiente cast seguramente llevará a resultados 
 +impredescibles: 
 + 
 +<code> 
 +  int h(int x, double y); 
 +  int (*f)(double x, int k)= (int (*)(double x, int k)) h; 
 +</code>
  
-Otra forma de hacer esto mismo:+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: 
 + 
 +<code> 
 +  int n= (*f)(3.14, 1); 
 +  int m= (*f)(3, 1.5); 
 +</code> 
 + 
 +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:
  
 <code> <code>
Línea 89: Línea 128:
 </code> </code>
  
-Lo cual es mucho más legible.+Lo cual es más legible.
  
-Por último también se puede usar una función de comparación de strings propia que +=== Tercer ejemplo de uso === 
-reciba como parámetros punteros a void:+ 
 +Este ejemplo ordena los strings por valor numérico:
  
 <code> <code>
-  int mistrcmp(void *p1, void *p2) { +  int numcmp(void *s1, void *s2/* compara numéricamente */ 
-    char *s1= (char*)p1+    int i1, i2; 
-    char *s2= (char*)p2+ 
-    return strcmp(s1s2);+    i1atoi((char*)s1)
 +    i2= atoi((char*)s2); 
 + 
 +    return i1<i2? -1 : i1==i2 ? 0 : 1; 
 +  } 
 + 
 +  int main(int argc, char **argv
 +    int j; 
 + 
 +    qsort((void**)argv1, argc-1, numcmp); 
 + 
 +    for (j= 1; j<argc; ++j) 
 +      printf("%s\n", argv[j]);
   }   }
 </code> </code>
  
-Ahora se puede llamar a qsort especificando como comparador mistrcmp sin tener +Estudie en los [[http://users.dcc.uchile.cl/~lmateu/CC3301/apuntes/LenguajeC/#19|apuntes de Patricio Poblete]] 
-que aplicar un complicado cast:+un programa que recibe la opción "-n" para seleccionar el criterio de ordenamiento numérico, mientras que 
 +por omisión ordena alfabéticamente. 
 + 
 +==== Ordenar un arreglo de enteros ==== 
 + 
 +¿Cómo se podría usar qsort para ordenar un arreglo de enteros?  Por ejemplo tenemos este arreglo:
  
 <code> <code>
-    qsort((void**)argv1argc-1, mistrcmp);+  int a[6]= {37, 8, 10, 1, 2};
 </code> </code>
  
-=== Segundo ejemplo de uso ===+Hay 2 formas: la elegante y la abreviada.  Primero veamos la elegante.  En teoría no 
 +podemos usar directamente el arreglo de enteros porque qsort recibe un arreglo de punteros, 
 +no enteros.  Entonces tenemos que fabricar un arreglo adicional que contenga punteros a los enteros:
  
 <code> <code>
-  int numcmp(void *s1, void *s2) /* compara numericamente */ { +  int *ap[6]= &a[0]&a[1], &a[2], &a[3], &a[4], &a[5] }; 
-    int i1i2;+</code>
  
-    i1= atoi((char*)s1); +Y ahora programamos la función de comparación y el main:
-    i2= atoi((char*)s2);+
  
-    return i1<i2-1 i1==i21;+<code> 
 +  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, 0, 5, intcmp); /* Ojo: ordena el arreglo ap, no a */
 +    for (i= 0; i<6; i++)
 +      printf("%d\n", *ap[i]);
 +    return 0;
 +  }
 +</code>
  
-  main() { +El cast ''(void%%**%%)ap'' es necesario porque el tipo de ap es ''int%%**%%'' pero qsort requiere ''void%%**%%''.
-    char s[ANCHO]; +
-    char *linea[MAX]; +
-    int i, j;+
  
-    for (i0; fgets(s, ANCHO, stdin)!=NULL; ++i) +=== Forma abreviada ===
-      linea[i]strdup(s);+
  
-    qsort((void **)linea, 0, i-1, numcmp);+La forma abreviada de ordenar un arreglo de enteros es hacerle creer a qsort que los enteros son punteros.
  
-    for (j= 0; j<i; ++j+<code> 
-      fputs(linea[j], stdout);+  /* 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, 0, 5, intcmp); 
 +    for (i= 0; i<6i++) 
 +      printf("%d\n", a[i]); /* esta vez si ordena a */ 
 +    return 0;
   }   }
 </code> </code>
  
-Estudie en los [[http://users.dcc.uchile.cl/~lmateu/CC3301/apuntes/LenguajeC/#19|apuntes de Patricio Poblete]] +Este truco solo funciona en arquitecturas en donde int sea del mismo tamaño que un puntero, i.e. x86 de 32 bits. 
-un programa que recibe la opción "-npara seleccionar el criterio de ordenamiento numérico, mientras que +No funcionará en amd64 por ejemplo.  Esta es una de las tantas razones por las que los programas que corren 
-por omisión ordena alfabéticamente.+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.  El tructo está en vestir 
 +los enteros como punteros en el arreglo que recibe qsort: 
 + 
 +<code> 
 +  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("%ld\n", (long)a[i]); /* esta vez si ordena a */ 
 +    return 0; 
 +  } 
 +</code> 
 + 
 +En Windows de 64 bits el tipo long es de 32 bits como 
 +se explica [[http://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows|acá]] 
 +y por lo tanto no coincide con el tamaño de un puntero.  Esto podría significar que el compilador 
 +entregue algún warning.  Pero a pesar del warning el ordenamiento debería hacerse correctamente. 
 + 
 +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://users.dcc.uchile.cl/~lmateu/CC3301/controles/ex-131.pdf|examen de 2013/1]]. 
 +  * Resuelva la pregunta 1 del [[http://users.dcc.uchile.cl/~lmateu/CC3301/controles/c2-131.pdf|control 2 de 2013/1]].  Pruebe su solución usando el archivo [[http://users.dcc.uchile.cl/~lmateu/CC3301/download/colapri.zip|colapri.zip]].  Para la parte a.- complete el archivo p1a.c.  Verifique el resultado con el comando "make test-p1a".  Luego resuelva la parte b.- en el archivo ordenar.c y verifique su funcionamiento con el comando "make test-p1b"
funciones2.1365798141.txt.gz · Última modificación: 2013/04/12 20:22 por lmateu