2010-05-08 14 views
54

¿Por qué y cómo desreferenciar un puntero a una función simplemente "no hacer nada"?¿Cómo ocurre la desreferenciación de un puntero de función?

Esto es lo que estoy hablando:

#include<stdio.h> 

void hello() { printf("hello"); } 

int main(void) { 
    (*****hello)(); 
} 

Desde un comentario sobre here:

punteros de función eliminar la referencia simplemente bien, pero la función designador resultante será inmediatamente convertido de nuevo a un puntero de función


Y a partir de una respuesta here:

Desreferenciar (en la forma de pensar) del puntero de función significa: acceso a una memoria CÓDIGO ya que sería una memoria de datos.

Puntero a la función no debe ser desreferenciado de esa manera. En cambio, se llama .

Usaría un nombre "dereference" side junto con "call". Está bien.

De todos modos: C está diseñado de tal manera que tanto nombre de la función identificador como así como puntero de la función de mantenimiento variable significa lo mismo: la dirección de memoria de código . Y permite saltar a esa memoria utilizando la sintaxis de call() ya sea en un identificador o variable.


Cómo exactamente hace desreferencia de un puntero de función de trabajo?

Respuesta

44

No es la pregunta correcta. Para C, al menos, la pregunta correcta es

¿Qué ocurre con un valor de función en un contexto rvalue?

(Un contexto rvalue es en cualquier lugar de un nombre u otra referencia aparece donde debe ser utilizado como un valor, en lugar de una ubicación — básicamente en cualquier lugar excepto en el lado izquierdo de una asignación. El nombre en sí proviene de el derecho -lado de la mano de una asignación.)

OK, entonces, ¿qué ocurre con un valor de función en un contexto rvalue? Se convierte inmediata e implícitamente en un puntero al valor de la función original. Si desreferencia ese puntero con *, vuelve a obtener el mismo valor de función, que se convierte inmediata e implícitamente en un puntero. Y puedes hacer esto tantas veces como quieras.

dos experimentos similares se puede probar:

  • ¿Qué pasa si usted referencia a un puntero de función en un contexto lvalue — el lado izquierdo de una asignación. (La respuesta será sobre lo que espera, si se tiene en cuenta que las funciones son inmutables.)

  • Un valor de matriz también se convierte en un puntero en un contexto lvalue, pero se convierte en un puntero a la elemento tipo, no a un puntero a la matriz. Desreferenciarlo, por lo tanto, le dará un elemento, no una matriz, y la locura que muestra no se produce.

Espero que esto ayude.

P.S. En cuanto a por qué un valor de función se convierte implícitamente en un puntero, la respuesta es que para aquellos de uso que usan punteros a funciones, es una gran conveniencia no tener que usar & en todas partes. También hay una doble ventaja: un puntero de función en la posición de llamada se convierte automáticamente a un valor de función, por lo que no es necesario escribir * para llamar a través de un puntero de función.

P.P.S. A diferencia de las funciones C, las funciones C++ pueden estar sobrecargadas, y no estoy calificado para comentar cómo funciona la semántica en C++.

+0

¿Podría elaborar más sobre "... implícitamente convertido en un puntero al valor de la función original"? ¿Te refieres al valor de retorno de la función? Si es así, ¿significa que el compilador guarda automáticamente ese valor de retorno como valor l, aunque el valor de retorno de la función era un valor r? ¡Gracias! – shorttermmem

5

C++ 03 § 4.3/1:

Un lvalue de tipo de función T puede ser convertido a un valor p de tipo “puntero a T.” El resultado es un puntero a la función.

Si intenta realizar una operación no válida en una referencia de funciones, tales como el * operador unitario, lo primero que intenta el idioma es una conversión estándar. Es como convertir un int al agregarlo a un float. El uso de * en una referencia de función hace que el idioma tome su puntero, que en su ejemplo es el cuadrado 1.

Otro caso donde esto se aplica es cuando se asigna un puntero a la función.

void f() { 
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr. 
    recurse(); // call operator is defined for pointers 
} 

Tenga en cuenta que este no lo hace trabajo hacia otro lado.

void f() { 
    void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref. 
    recurse(); // OK - call operator is *separately* defined for references 
} 

variables de referencia de funciones son agradables porque (en teoría, nunca he probado) sugerencia para el compilador que una rama indirecta puede ser innecesario, si es inicializado en un ámbito que lo contiene.

En C99, la desreferenciación de un puntero de función produce un designador de función. §6.3.2.1/4:

Un designador de función es una expresión que tiene un tipo de función. Excepto cuando es el operando del operador sizeof o el operador unitario &, un designador de función con tipo '' función que devuelve tipo '' se convierte a una expresión que tiene el tipo '' puntero a función que devuelve tipo ''.

Esto es más como la respuesta de Norman, pero notablemente C99 no tiene ningún concepto de valores.

+0

"_on a function reference_" En realidad, ** una expresión no puede tener el tipo de referencia **. Una expresión puede ser rvalue o lvalue. – curiousguy

2

Ponte en la piel del escritor del compilador. Un puntero a función tiene un significado bien definido, es un puntero a una burbuja de bytes que representa el código máquina.

¿Qué hace cuando el programador desreferencia un puntero a la función? ¿Toma los primeros (u 8) bytes del código máquina y los reinterpreta como un puntero? Las probabilidades son alrededor de 2 mil millones a uno que esto no funcionará. ¿Declaras UB? Ya hay mucho de eso dando vueltas. ¿O simplemente ignoras el intento? Tu sabes la respuesta.

+4

Si yo fuera el compilador, lo haría ilegal. Esta es una respuesta algo engañosa. – rpjohnst

1

¿Cómo funciona exactamente la desreferenciación de un puntero de función?

Dos pasos. El primer paso es en tiempo de compilación, el segundo en tiempo de ejecución.

En la etapa uno, el compilador ve que tiene un puntero y un contexto en el que se eliminan las referencias que puntero (tal como (*pFoo)()) por lo que genera código para esa situación, el código que será utilizado en el paso 2.

En el paso 2, en tiempo de ejecución el código se ejecuta. El puntero contiene algunos bytes que indican qué función se debe ejecutar a continuación. Estos bytes están de alguna manera cargados en la CPU. Un caso común es una CPU con una instrucción explícita CALL [register]. En dichos sistemas, un puntero a la función puede ser simplemente la dirección de una función en la memoria, y el código de desapego no hace más que cargar esa dirección en un registro seguido de una instrucción CALL [register].

Cuestiones relacionadas