2009-04-16 13 views
5

Hay las siguientes declaraciones:una pregunta acerca de la función de puntero en C

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *)); 
int numcmp(char *, char *); 
int strcmp(char *s, char *t); 

Entonces, en algún lugar en el programa existe la siguiente llamada:

qsort((void**) lineptr, 0, nlines-1, 
        (int (*)(void*,void*))(numeric ? numcmp : strcmp)); 

(No haga caso de los tres primeros argumentos y numeric) .

me preguntan qué es lo siguiente:

(int (*)(void*,void*))(numeric ? numcmp : strcmp) 

entiendo que qsort está esperando un "puntero a función que recibe dos void punteros y devuelve un int" ya que es cuarto argumento, pero cómo lo que está escrito arriba satisface eso? Me parece un tipo de elenco porque está hecho de dos paréntesis, pero sería un lanzamiento muy extraño. Porque toma una función y hace de esta función un "puntero a la función que obtiene dos punteros void y devuelve un int". Que no tiene sentido
(Seguí aquí la regla de que un tipo type entre paréntesis antes de que una variable promueva la variable a ese tipo).

Así que creo que me equivoco, tal vez alguien me puede decir cómo leer esto, ¿cuál es el orden?

+0

¿Por qué no tiene sentido? Me parece bien. – cadrian

+0

Porque tomas una función y la vuelves inútil. Se convierte en solo un puntero. Ya no es una función. Además, ¿qué se está pasando a qsort? solo un puntero que se supone que apunta a la función, pero nadie nunca le dijo a qué función. También creo que convertir un func 'en un puntero se está reduciendo –

+0

Un puntero a una función es * un puntero de función * ... que es perfectamente utilizable como una función, gracias ... – Varkhan

Respuesta

4

Lo que está sucediendo aquí es realmente un elenco. Vamos a ignorar el ternario por un segundo y pretender que numcmp siempre se utiliza. Para el propósito de esta cuestión, las funciones pueden actuar como punteros a funciones en C. Así que si nos fijamos en el tipo de numérico en realidad es

(int (*)(int*,int*)) 

Para que esto sea utilizado correctamente en qsort necesita tener nula parámetros. Como todos los tipos tienen el mismo tamaño con respecto a los parámetros y los tipos de devolución, es posible sustituirlos por el otro. Todo lo que se necesita es un molde para hacer feliz al compilador.

(int (*)(void*,void*))(numcmp) 
+1

Quiere hablar de numcmp, no numérico –

+0

@Brian, gracias. Actualicé la publicación – JaredPar

+0

Usted escribió que "el tipo de numérico es en realidad (int (*) (int *, int *))". No lo entiendo El tipo de expresión ternaria es una función y lo que usted escribió es un puntero a una función. –

0

Su lógica es correcta, creo. De hecho, se convierte en "puntero a la función que obtiene dos punteros vacíos y devuelve un int" que es el tipo requerido por la firma del método.

4

Te has perdido el truco aquí - la parte

(numeric ? numcmp : strcmp) 

está utilizando el operador ternario para elegir la que función que se está llamando dentro de qsort. Si los datos son numéricos, usa numcmp. Si no, usa strcmp. Una implementación más legible se vería así:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp); 
qsort((void**) lineptr, 0, nlines-1, comparison_function); 
+2

Realmente no estoy convencido de que sea más legible :) –

+0

@Sean Bright: Realmente no creo que haya * una * forma de hacerlo legible. La única ventaja que veo en él es que usa un nombre de variable (función de comparación) que intenta explicar lo que está sucediendo. –

+0

Su sintaxis es incorrecta, aunque es cierto que la sintaxis para los indicadores de función es extremadamente incómoda y confusa. –

0

Tanto numcmp y strcmp son punteros a funciones que toman dos char* como parámetros y devuelve un int. La rutina qsort espera un puntero a una función que toma dos void* como parámetros y devuelve un int. De ahí el elenco. Esto es seguro, ya que void* actúa como un puntero genérico.Ahora, a la lectura de la declaración: Tomemos sustrcmp 's declaración:

int strcmp(char *, char *); 

El compilador lee como strcmp es en realidad:

int (strcmp)(char *, char *) 

una función (en descomposición a un puntero a una función en la mayoría de los casos) que toma dos argumentos char *. Por lo tanto, el tipo del puntero strcmp es:

int (*)(char *, char *) 

Por lo tanto, cuando se necesita para echar otra función para que sea compatible con strcmp que tendría que utilizar lo anterior como el tipo para echar a.

Del mismo modo, dado que el argumento de comparación de qsort toma dos void * s y, por lo tanto, el reparto impar!

4

Puede hacerlo sin la función de fundido de puntero. Aquí está how. En mi experiencia, en la mayoría de los lugares, si estás usando un yeso, lo estás haciendo mal.

1

que probablemente leerlo así:

typedef int (*PFNCMP)(void *, void *); 

PFNCMP comparison_function; 

if (numeric) 
{ 
    comparison_function = numcmp; 
} 
else 
{ 
    comparison_function = strcmp; 
} 

qsort((void**) lineptr, 0, nlines-1, comparison_function); 

El ejemplo de la pregunta tiene un caso explícito.

3

Tenga en cuenta que la definición estándar de qsort() incluye const:

void qsort(void *base, size_t nmemb, size_t size, 
      int (*compar)(const void *, const void *)); 

Nota que el comparador de cadenas da dos, no char * '' valores '' char ** valores.

escribo mis comparadores de modo que los moldes son innecesarias en el código de llamada:

#include <stdlib.h> /* qsort() */ 
#include <string.h> /* strcmp() */ 

int num_cmp(const void *v1, const void *v2) 
{ 
    int i1 = *(const int *)v1; 
    int i2 = *(const int *)v2; 
    if (i1 < i2) 
     return -1; 
    else if (i1 > i2) 
     return +1; 
    else 
     return 0; 
} 

int str_cmp(const void *v1, const void *v2) 
{ 
    const char *s1 = *(const char **)v1; 
    const char *s2 = *(const char **)v2; 
    return(strcmp(s1, s2)); 
} 

Forzar a la gente a escribir moldes en el código usando sus funciones es feo. No lo hagas

Las dos funciones que escribí coinciden con el prototipo de función requerido por la norma qsort(). El nombre de una función cuando no está seguido por paréntesis es equivalente a un puntero a la función.

Usted encontrará en código antiguo, o el código escrito por los que se criaron en los compiladores de más edad, que los punteros a funciones se utilizan usando la notación:

result = (*pointer_to_function)(arg1, arg2, ...); 

en estilo moderno, que está escrito:

result = pointer_to_function(arg1, arg2, ...); 

Personalmente, me parece más clara la desreferencia explícita, pero no todos están de acuerdo.

2

Como otros han señalado, por

(int (*)(void*,void*))(numeric ? numcmp : strcmp) 

entonces el siguiente es un tipo fundido

(int (*)(void*,void*)) 

y la expresión es

(numeric ? numcmp : strcmp) 

declaraciones de C pueden ser bastante difícil de leer, pero es posible aprender El método es comenzar en la parte interna y luego ir a la derecha un paso, luego a la izquierda un paso, continuar hacia la derecha, izquierda, derecha, izquierda, etc. hacia afuera hasta que termine. No se cruza fuera de un paréntesis antes de que todo lo que está dentro haya sido evaluado. Por ejemplo, para el tipo de elenco anterior, (*) indica que se trata de un puntero. El puntero era lo único dentro del paréntesis, entonces evaluamos hacia el lado derecho fuera de él. (void*,void*) indica que es un puntero a una función con dos argumentos de puntero. Finalmente, int indica el tipo de devolución de la función. El paréntesis externo lo convierte en un tipo de molde. Actualización: dos artículos más detallados: The Clockwise/Spiral Rule y Reading C Declarations: A Guide for the Mystified.

Sin embargo, la buena noticia es que a pesar de lo anterior es muy útil saber, hay una manera muy sencilla de hacer trampa: el programa cdecl puede convertir de C a la descripción del Inglés y viceversa:

cdecl> explain (int (*)(void*,void*)) 
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int 
cdecl> declare my_var as array 5 of pointer to int 
int *my_var[5] 
cdecl> 

Ejercicio: ¿Qué tipo de variable es i?

int *(*(*i)[])(int *) 

respuesta en rot13 en caso de que no tiene instalado cdecl en su máquina (pero que realmente debería!):

pqrpy> rkcynva vag *(*(*v)[])(vag *) 
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag 
pqrpy> 
2

El que escribió ese fragmento de código estaba tratando de ser demasiado inteligente. En su mente, probablemente él piense que está siendo un buen programador haciendo un inteligente "one-liner". En realidad, él está creando un código que es menos legible y es detestable para trabajar a largo plazo y debe volver a escribirse en una forma más obvia similar al código de Harper Shelby.

Recuerde el dicho de Brian Kernighan:

La depuración es dos veces más duro que escribir el código en el primer lugar. Por lo tanto, si escribe el código como inteligentemente como sea posible, está, por definición , no lo suficientemente inteligente como para depurar .


que hacer un montón de rendimiento de codificación con plazos críticos en tiempo real duros ... y todavía no han visto un lugar donde una densa de una sola línea es el adecuado.

Incluso he echado a perder la compilación y el control del asm para ver si el one-liner tiene una mejor implementación compilada de asm pero nunca he encontrado que el one-liner lo valga.

+1

Olvidé mencionar que era un fragmento de código de K & R, así que tal vez Kernighan se contradijo a sí mismo allí. –

+0

insertar la boca en el pie ... ahora. –

+0

no es astuto utilizar la biblioteca estándar correctamente ... –

Cuestiones relacionadas