2012-06-07 16 views
12

que vamos gcc compilar el siguiente ejemplo usando -Wall -pedantic:Cómo imprimir la dirección de una función?

#include <stdio.h> 

int main(void) 
{ 
    printf("main: %p\n", main); /* line 5 */ 
    printf("main: %p\n", (void*) main); /* line 6 */ 

    return 0; 
} 

me sale:

main.c:5: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘int (*)()’ 
main.c:6: warning: ISO C forbids conversion of function pointer to object pointer type 

Línea 5 hizo que mi cambio del código, como en la línea 6.

¿Cuál falto de eliminar la advertencia al imprimir la dirección de una función?

+1

No estoy seguro si está disponible para usted, pero es posible investigar el uso de register_printf_function a [definir su propio carácter especial y un convertidor de formato] (http://www.gnu.org/software/libc/manual/html_node/ Customizing-Printf.html # Customizing-Printf). –

+0

El problema no es que sea "peligroso". El problema es que la conversión no está definida por el lenguaje C y, por lo tanto, no se puede usar para conformar el código C. Puede lanzar a través de un tipo de entero intermedio (resultados definidos por la implementación) siempre que sepa que existe uno que puede contener tanto punteros de función como de objeto). –

+0

@BobJarvis ¿Tiene alguna idea de cómo evitar 'gcc' quejarse del carácter de tipo de conversión recientemente introducido (' advertencia: carácter de tipo de conversión desconocido 'P' en formato') al compilar con la opción '-Wall'? Pero esta también es otra historia ... – alk

Respuesta

12

Esta es esencialmente la única forma portátil de imprimir un puntero a función.

size_t i; 
int (*ptr_to_main)() = main; 
for (i=0; i<sizeof ptr_to_main; i++) 
    printf("%.2x", ((unsigned char *)&ptr_to_main)[i]); 
putchar('\n'); 
+0

Me gusta esto, aunque ignora silenciosamente cualquier problema de endianess ... ;-) – alk

+0

No había pensado en esto. +1. –

+1

Intentando generalizar este enfoque, me pregunto si se garantiza que los punteros de función de diferentes tipos tendrán el mismo tamaño, aunque una respuesta (http://stackoverflow.com/a/189126/694576) a la pregunta mencionada por larsman me hace dudar esto ... - de todos modos esta es otra historia. – alk

4

De hecho, esta idea no es portátil, ya que algunos sistemas utilizan punteros de diferentes tamaños para el código y los datos.

Lo que realmente necesita es conocimiento específico de la plataforma sobre cuán grande es un puntero de función, y un molde para un tipo integral de ese tamaño. Desafortunadamente, no creo que nadie haya estandarizado un intfuncptr_t análogo al intptr_t que puede contener cualquier puntero de datos.


Como señala R. in his answer, siempre se puede tratar el puntero como una matriz de (posiblemente signed o unsigned) char, de esta manera no es necesario ningún tipo integral del tamaño correcto.

+0

No hay ningún tipo de datos integral; solo 'char sin signo' (o probablemente' char' aunque sucio y dudoso). Cualquier otro tipo es una violación de alias y es probable que los compiladores modernos emitan código incorrecto que no hace lo que usted quería. –

+0

@R ..: una conversión no puede ser una violación de aliasing. Solo si tiene dos lvalues ​​de diferente tipo que hacen referencia a la misma ubicación, ¿puede haber una violación de aliasing? Tenga en cuenta que estoy hablando de 'reinterpret_cast (fnptr)', no '* reinterpret_cast (& fnptr)'. Su respuesta implica aliasing legal, el mío no implica aliasing en absoluto. Oh, ahora veo, estás hablando de mi explicación de tu método. Sí, 'char' es necesario allí, lo actualizaré. –

+1

Hay '(uintmax_t) & main', quizás con un' static_assert (sizeof (uintmax_t)> = sizeof (& main)); 'para garantizar que no haya UB sin diagnosticar debido a una conversión fuera de rango –

4

Está justo allí en la advertencia: ISO C prohíbe la conversión de un puntero a un tipo de puntero a objeto, que incluye void*. Vea también this question.

Simplemente no puede imprimir la dirección de una función de forma portátil, por lo que no puede deshacerse de la advertencia.

Puede imprimir un puntero de función utilizando la sugerencia de @R ..

+2

Por POSIX, la representación de todos los punteros son iguales, por lo que en un sistema POSIX, puede 'memcpy' desde una variable de puntero a una variable de puntero de datos y luego imprimir eso. Y, por supuesto, puede * siempre * imprimir la representación de cualquier tipo byte a byte, sin dependencia de POSIX. –

4

Si bien convertir un puntero a un puntero void es técnicamente peligroso, la conversión de los punteros a los punteros void se usa en el estándar POSIX, por lo que es casi seguro que funcione en la mayoría de los compiladores.

Buscar dlsym().

+3

La mayoría del código C se ejecuta fuera de un entorno compatible con POSIX. –

+1

La asignación del valor de retorno de 'dlsym()' a una variable de puntero de función declarada correctamente conduce a un problema similar, pero al revés. – alk

+1

Por lo tanto, básicamente, POSIX-2001 es incompatible con las definiciones ABI que usan un tamaño diferente para los punteros de función que para un 'void *'. Bueno saber. – cmaster

Cuestiones relacionadas