2008-11-03 19 views
6

Al usar qsort en C, se pasa una función de comparación, p.Punteros de función en el operador de dirección C "innecesarios"

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

el prototipo muy de qsort espera un int (*) (const void *, const void *) por lo que llaman:

qsort(..., cmp); 

pero es igualmente válida para llamar:

qsort(..., &cmp); 

y esto es lo que tendríamos que hacer si aprobamos una función de miembro estática en C++. Kernighan & Ritchie (2da Edición, 5.11 "Punteros a las Funciones" p119) establece que "dado que se conoce que [cmp] es una función, el operador & no es necesario, de la misma manera que no es necesario antes del nombre de una matriz. "

¿Alguien más se siente un poco incómodo con esto (especialmente con respecto a la seguridad de tipo)?

Respuesta

8

Si se siente incómodo o no, no cambia el hecho de que C no se considera un lenguaje seguro para el tipo de letra. Caso de ejemplo:

int main() 
{ 
    int integer = 0xFFFFFF; 
    void (*functionPointer)() = (void(*)())integer; 

    functionPointer(); 

    return 0; 
} 

Esto es completamente válido en tiempo de compilación, pero obviamente no es seguro.

+3

Cierto, pero usar ese molde es una cachiporra para decirle al compilador "créeme, sé lo que estoy haciendo, incluso si piensas lo contrario". –

3

Es un detalle de sintaxis. Si no le gusta no usar & para indicar un puntero de función, utilícelo siempre. Si te gusta guardar las pulsaciones de teclas adicionales y no ser tan explícito en tu codificación, entonces aprovéchalo.

En cuanto a la seguridad del tipo, no veo cómo hace la diferencia. El compilador recibe una función como argumento y puede determinar cuál es la firma de la función a partir del nombre. El hecho de que no lo esté llamando (con la sintaxis() brinda todas las indicaciones que necesita el compilador de que desea un puntero a función en esta ubicación. El & es solo una sutileza sintáctica para indicar a los humanos que están mirando un puntero de alguna descripción.

Si está utilizando C++, le sugiero que consulte los funtores de refuerzo como un mejor método para pasar las funciones de todos modos. Estas entidades encantadoras permiten una sintaxis unificada y bastante clara para todas las funciones en C++ :)

1

No es tanto una sensación de incomodidad como un disgusto por las opciones de sintaxis válidas pero conflictivas. O bien cmp es un puntero, y debe tratarse consistentemente como tal, o es de algún otro tipo, lo que, en mi humilde opinión, es sintácticamente engañoso.

Yendo un poco más lejos, también me requiero una llamada ya sea para eliminar la referencia al puntero (para resaltar el hecho de que es un puntero) o no (porque un nombre de función es un puntero) ... pero no permitirá ambos.

Los punteros de función pueden ser extremadamente potentes, sin embargo, parecen ser una fuente de confusión tanto para los programadores nuevos como para los experimentados. Parte de la dificultad podría aliviarse requiriendo una sintaxis única y significativa que aclare su uso, en lugar de ocultándolo.

4

Sospecho que solo te sientes incómodo porque no estás acostumbrado a codificar como "cerca del metal" como lo permite C. La razón por la que C no requiere el operador de dirección de las funciones es que no hay nada que realmente pueda hacer con funciones que no sean llamarlas o pasarlas.

En otras palabras, no existe una definición sensata para manipular el 'valor' de una función.

Mientras que con ints, puede sumar o restar del valor, esto no tiene sentido para las funciones.

En cualquier caso, C es anterior tanto a C++ como a su propia estandarización: la K & R C original ni siquiera tenía prototipos y ANSI tenía que asegurarse de que su estandarización no rompiera el código existente tanto como fuera posible.

+1

seguro que hay. Puedes hacer functionptr ++ y hacer cosas malas, malas en la pila. ;) –

1

Tenga en cuenta que lo mismo vale para los punteros de funciones de 'desreferencia' (es decir, llamada); usted no tiene que

(*funcptr)(arg1, arg2); 

como

funcptr(arg1, arg2); 

será suficiente.

5

Bueno, la respuesta es que pasar una función por valor produce un puntero a la función, del mismo modo que pasar una matriz por valor produce un puntero a su primer elemento. uno dice que la matriz y la función "decaen". solo hay unas pocas ocasiones donde esa decadencia no ocurre. por ejemplo sizeof (array) produce el tamaño de la matriz, no el de su primer puntero de elemento. sizeof (función) no es válida (las funciones no son objetos), tiene que hacer sizeof (función &). Otras veces son vinculantes para las referencias:

void baz(); 

void foo(void (&bar)()) { 
    bar(); 
} 

// doesnt work, since a reference to a function is requested. 
// you have to pass 'bar' itself, without taking its address 
// explicitely. 
foo(&baz); 

Esto, por cierto todo es la razón por la que puede hacer

template<typename T, int N> 
void ByRef(T (&foo)[N]) { 
    ... 
} 

desde la matriz no se descompone aún cuando se consideran los parámetros de referencia.

Cuestiones relacionadas