2012-04-19 10 views
9

Lo primero de todo es que todo lo que un compilador puede detectar de inmediato si se hace incorrectamente.Cómo son inseguros los punteros a las funciones

Ahora, he oído que los punteros a las funciones no son seguros, pero cada vez que intento usarlos incorrectamente, el compilador me informa de los errores. Entonces, ¿cómo es el tipo inseguro?

Ej Este es un prototipo de función que se lleva en un puntero de función

void SortElements(void* MyArray, unsigned int iNumofElems,size_t size, int(*compare_funct)(void* First,void* SecondElem)) 

he definido algunas funciones para pasar a ella como:

int MySortAsc(void* First, void* Second); 
void MyFunct2(); 
void MyFunct3(void* First); 

El código sólo se compila para:

SortElements(MyArray, 10, sizeof(DataType), &MySortAsc); //Compiles 
SortElements(MyArray, 10, sizeof(DataType), &MyFunct2); //Fails 

¿Alguna idea de cómo puedo usar mal los punteros a las funciones aquí?

¿Es debido a esto:

void (*functionPointer)(); 
... 
int integer = 0xFFFFFFFF;  
functionPointer = (void(*)())integer;  
functionPointer(); 

Respuesta: Lo que pude ver es que los punteros de función en C++ son seguros tipo. Por supuesto, se pueden usar de manera insegura al lanzarlo de manera incoherente, pero eso no los convierte en una razón para que los llamen inseguros. Los delegados de .NET están fuertemente tipados también y para mí parece que ambos son seguros.

+3

¿Dónde lo vio descrito como inseguro? Ver la fuente ayudará a entender la intención del escritor. – selalerer

+0

"Typesafe" significa que la corrección de un fragmento de código se debe únicamente a su tipificación estática, y no depende de * valores * particulares de variables. Por ejemplo, 'printf' no es seguro, ya que su corrección depende del * valor * de la cadena de formato, así como de sus tipos de argumentos. –

+0

¿Por qué se dice entonces que los delegados de .NET son punteros de función de tipo seguro en comparación con los pionters de función normal? –

Respuesta

10

Entonces, ¿cómo es el tipo inseguro?

void SortElements(void* MyArray,    // what type is pointed here? 
        unsigned int N,    // Are there really N elements? 
        size_t size,    // Is the size correct? 
        int(*cmp)(void*,void*)); // Is this the correct function? 

El código que se presente es de tipo inseguro, no por el puntero de función, sino más bien debido a la utilización de void* tanto en el SortElements firma y la firma del puntero de función.

La razón para que esto no es seguro es porque la persona que llama tiene toda la responsabilidad de pasar los argumentos correctos, y el compilador no puede garantizar que el puntero puntos a una región de memoria contigua que sostiene iNumofElems cada uno de los cuales tiene la size ofrecida en La interfaz.Si el programador comete un error, el compilador no podrá ayudarlo, si un mantenedor modifica el tipo almacenado en la matriz (cambios de tamaño) o la cantidad de elementos, el compilador no podrá detectarlo y le dirá que necesita actualizar la llamada al SortElements. Finalmente, dado que el puntero de función que se pasa también usa void*, la firma de un comparador que compara manzanas y peras es exactamente la misma, y ​​el compilador no puede ayudar si pasa el puntero de función incorrecto.

struct Apple { 
    int weight; 
}; 
struct Pear { 
    double weight; 
}; 
int compare_pears(void * pear1, void * pear2) { 
    return static_cast<Pear*>(pear1)->weight - static_cast<Pear*>(pear2)->weight; 
} 
int main() { 
    Apple apples[10]; 
    SortElements(apples, 20, sizeof(Pear), compare_pears); 
} 

Mientras que el compilador es capaz de verificar que la firma del puntero de función coincide con la firma que necesita la función, la función de puntero en sí no es seguro, y permite pasar un comparador para básicamente cualquier cosa.

Compare eso con esta otra alternativa:

template <typename T, std::size_t N> 
void SortElements(T (&array)[N], int (*cmp)(T const &, T const &)); 

Aquí el compilador inferirá el tipo de los elementos T y el tamaño de la matriz N de la llamada. No es necesario pasar el tamaño de T, ya que el compilador lo sabe. La función de comparador que se pasa a esta versión de SortElements está fuertemente tipada: toma dos referencias constantes al tipo del elemento almacenado en la matriz y devuelve un int. Si lo intentamos en el programa anterior:

int compare_pears(Pear const & lhs, Pear const & rhs); 
int compare_apples(Apple const & l, Apple const & r); 
Apple array[10]; 
//SortElements(array, compare_pears); // Error!!!! 
SortElements(array, compare_apples); // Good! 

No se puede confundir el tamaño de la matriz o el tamaño de los elementos, si alguien cambia el tipo Apple, el compilador recogerlo, si el tamaño de la cambios de matriz, el compilador lo recogerá. No se puede confundir el comparador que se pasa a la función ya que el compilador también la recogerá. Ahora el programa es seguro, incluso si utiliza punteros a funciones (que pueden tener un impacto en el rendimiento ya que inhiben la alineación, por lo que std::sort suele ser más rápido que qsort)

+0

Esta es una gran explicación. Por naturaleza, los punteros a las funciones son seguros y dependen de cómo se usan, lo que puede hacer que hagan cosas inseguras, como en el caso de mi ejemplo. –

+5

No, yo no diría eso. Los punteros de función son tipo seguros. Cualquier código, no limitado a punteros a funciones, puede ser inseguro cuando comienza a transmitir. Es el casting lo que hace que las cosas no sean seguras. Los punteros de función no tienen absolutamente ninguna propiedad especial en relación con la seguridad del tipo. –

+3

@FrankQ.Los problemas de seguridad de tipo en su código provienen del diseño de la interfaz para las funciones, que elimina información de tipo, no del hecho de que está pasando indicadores de función. La eliminación de los tipos adecuados por medio de la conversión implícita a 'void *' implica que la carga de administrar qué tipo de cada bit en la memoria cae en manos del programador (conversiones) sin ninguna ayuda del compilador para verificar la corrección. También tenga en cuenta, no mencionado anteriormente, que la función solo puede administrar los datos como bits, y eso significa que generará un comportamiento indefinido para cualquier otra cosa que no sea POD. –

5

Los punteros de función son de hecho revisados ​​y son de tipo seguro.

6

Los punteros de función son seguros. Sin embargo, muchos entornos obligan al programador a la necesidad de modificarlos. Un lanzamiento incorrecto podría causar problemas significativos.

1

Los punteros de función se desaconsejan firmemente en nesC (un dialecto de C utilizado en TinyOs), por la razón de que dificultan la optimización. Aquí el análisis de código estático (o más bien la falta de su aplicabilidad) es una preocupación mayor que la seguridad de tipo, pero no estoy seguro de si estos problemas podrían confundirse.

Otro problema podría ser el uso de indicadores de función como controladores de eventos. Al utilizar un programador de eventos generales, es posible que desee abstraerse del tipo correcto, lo que significaría que podría tener la idea de almacenar apuntadores de función como void* solo por el bien de la modularidad. Este sería un ejemplo destacado del uso inseguro de punteros a función en lugar de uso de vinculación dinámica segura para tipos.

Cuestiones relacionadas