Pregunta

Hay las siguientes declaraciones:

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 no es la llamada siguiente:

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

(Ignorar los primeros tres argumentos y numeric).

Me pregunto qué es lo siguiente:

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

Yo entiendo que qsort está esperando un "puntero a función que recibe dos punteros void y devuelve un int" ya que es cuarto argumento, pero cómo lo que está escrito arriba satisface eso? Me parece como una especie de molde, ya que está hecho de dos paréntesis, pero eso sería un reparto muy extraño. Debido a que toma una función y esa función no tiene un "puntero a función que recibe dos punteros void y devuelve un int". Lo cual no tiene sentido.
(He seguido aquí la regla de que un tipo type entre paréntesis antes de una variable promueve la variable de ese tipo).

Así que creo que simplemente se equivocan, tal vez alguien puede decirme cómo leer esto, ¿cuál es el orden?

¿Fue útil?

Solución

¿Qué pasa aquí es de hecho un yeso. Vamos a ignorar el ternario para una segunda y pretender que numcmp se utiliza siempre. 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 para ser utilizado adecuadamente en qsort necesita tener parámetros vacíos. Debido a que los tipos aquí todos tienen el mismo tamaño con respecto a los parámetros y tipos de retorno, es posible sustituir por otro. Todo lo que se necesita es un molde para hacer feliz al compilador.

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

Otros consejos

Te has perdido el truco aquí - la parte

(numeric ? numcmp : strcmp)

está utilizando el operador ternario para elegir que función se llama en el interior de qsort. Si los datos es numérico, se utiliza numcmp. Si no, se utiliza strcmp. Una aplicació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);

Puede hacerlo sin el elenco puntero de función. Aquí es cómo . En mi experiencia, en la mayoría de lugares, si está utilizando un molde, lo estás haciendo mal.

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

Tenga en cuenta que el comparador de cadenas da dos valores '' char **, no los valores 'char *'.

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

Las dos funciones que escribí coinciden con el prototipo de la función requerida por la norma qsort(). El nombre de una función cuando no seguida de 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, ...);

En lo personal, creo que el eliminar la referencia explícita más clara, pero no todos están de acuerdo.

Quien escribió ese fragmento de código estaba tratando de ser demasiado inteligente . En su mente, él probablemente piensa que es ser un buen programador haciendo una inteligente "de una sola línea". En realidad, está haciendo el código que es menos legible y es desagradable para trabajar con en el largo plazo y debe ser reescrito en una forma más obvia similar al código de Harper Shelby.

Recuerde el dicho de Brian Kernighan:

  

La depuración es el doble que la escritura   el código en el primer lugar.   Por lo tanto, si se escribe el código como   inteligente posible, usted es, por   definición, no lo suficientemente inteligente como para depurar   a él.


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

Incluso me han metido un poco con la recopilación y comprobación de la ASM para ver si el de una sola línea tiene una aplicación asm mejor compilado pero nunca he encontrado el de una sola línea a valer la pena.

Como otros han señalado, por

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

a continuación, el siguiente es un tipo moldeado

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

y la expresión es

(numeric ? numcmp : strcmp)

declaraciones de C pueden ser muy difíciles de leer, pero es posible aprender. El método es empezar por la parte interior y luego a la derecha un paso, luego a la izquierda un paso, continuar derecha, izquierda, derecha, izquierda, etc hacia el exterior hasta que termine. No cruzar fuera un paréntesis antes de que todo el interior ha sido evaluada. Por ejemplo para el tipo fundido anteriormente, (*) indica es un puntero. Puntero fue el único dentro del paréntesis es así, se evalúa a la derecha fuera de ella. (void*,void*) indica que es un puntero a una función con dos argumentos de puntero. Finalmente int indica el tipo de retorno de la función. El paréntesis exterior hace que este tipo moldeado. Actualización: dos artículos más detallados: La derecha / Espiral Regla y lectura declaraciones de C:. Una Guía para la Mystified

Sin embargo, la buena noticia es que a pesar de lo anterior es muy útil saber, no es una forma muy sencilla de hacer trampa cdecl programa puede convertir de C a Descripción 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 *)

rot13 en caso de que no lo tiene instalado en su máquina cdecl (pero 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>

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

Su lógica es correcta, creo. De hecho, está lanzando a "puntero a función que recibe dos punteros void y devuelve un int", que es el tipo requerido por la firma del método.

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. Por lo tanto el reparto. Esto es seguro, ya que void* actúa como un puntero genérico. Ahora, a la lectura de la declaración: Tomemos su Declaración de strcmp:

 int strcmp(char *, char *);

El compilador lee como strcmp es en realidad:

 int (strcmp)(char *, char *)

una función (descomposición a un puntero a una función en la mayoría de los casos) que toma dos argumentos char *. Por consiguiente, el tipo de la strcmp puntero 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 type para echar a.

Del mismo modo, ya que el argumento comparador de qsort toma dos void *s y por lo tanto el elenco extraño!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top