¿Por qué las definiciones de puntero de función funcionan con cualquier número de símbolos de unión ‘&’ o asteriscos ‘*’?

7 minutos de lectura

1647555487 234 ¿Por que las definiciones de puntero de funcion funcionan con
Palanqueta

¿Por qué funciona lo siguiente?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}

1647555487 311 ¿Por que las definiciones de puntero de funcion funcionan con
james mcnellis

Hay algunas piezas en esto que permiten que todas estas combinaciones de operadores funcionen de la misma manera.

La razón fundamental por la que todo esto funciona es que una función (como foo) es implícitamente convertible en un puntero a la función. Esta es la razón por void (*p1_foo)() = foo; obras: foo se convierte implícitamente en un puntero a sí mismo y ese puntero se asigna a p1_foo.

el unario &, cuando se aplica a una función, produce un puntero a la función, al igual que produce la dirección de un objeto cuando se aplica a un objeto. Para punteros a funciones ordinarias, siempre es redundante debido a la conversión implícita de función a puntero de función. En todo caso, por eso void (*p3_foo)() = &foo; obras.

el unario *cuando se aplica a un puntero de función, produce la función a la que se apunta, al igual que produce el objeto al que se apunta cuando se aplica a un puntero normal a un objeto.

Estas reglas se pueden combinar. Considere su penúltimo ejemplo, **foo:

  • Primero, foo se convierte implícitamente en un puntero a sí mismo y el primero * se aplica a ese puntero de función, dando como resultado la función foo otra vez.
  • Luego, el resultado se vuelve a convertir implícitamente en un puntero a sí mismo y el segundo * se aplica, dando de nuevo la función foo.
  • Luego se convierte implícitamente en un puntero de función nuevamente y se asigna a la variable.

Puede agregar tantos *Como quieras, el resultado es siempre el mismo. Cuanto más *s, mejor.

También podemos considerar su quinto ejemplo, &*foo:

  • Primero, foo se convierte implícitamente en un puntero a sí mismo; el unario * se aplica, dando foo otra vez.
  • Entonces el & es aplicado a foodando un puntero a fooque se asigna a la variable.

los & Sin embargo, solo se puede aplicar a una función, no a una función que se haya convertido en un puntero de función (a menos, por supuesto, que el puntero de función sea una variable, en cuyo caso el resultado es un puntero a un puntero a -a-función; por ejemplo, podría agregar a su lista void (**pp_foo)() = &p7_foo;).

Esta es la razón por &&foo no funciona: &foo no es una función; es un puntero de función que es un valor r. Sin embargo, &*&*&*&*&*&*foo funcionaría, como lo haría &******&fooporque en ambas expresiones el & siempre se aplica a una función y no a un puntero de función rvalue.

Tenga en cuenta también que no es necesario utilizar el unario * para realizar la llamada a través del puntero de función; ambas cosas (*p1_foo)(); y (p1_foo)(); tener el mismo resultado, nuevamente debido a la conversión de función a puntero de función.

  • @Jimmy: Esas no son referencias a punteros de función, son solo punteros de función. &foo toma la dirección de foolo que da como resultado un puntero de función que apunta a foocomo cabría esperar.

    –Dennis Zickefoose

    1 de agosto de 2011 a las 0:59

  • no puedes encadenar & operadores para objetos ya sea: dado int p;, &p da un puntero a p y es una expresión de valor r; los & El operador requiere una expresión lvalue.

    –James McNellis

    1 de agosto de 2011 a las 1:15

  • Estoy en desacuerdo. Cuanto más *‘s, el menos alegre.

    – Seth Carnegie

    1 de agosto de 2011 a las 1:28


  • No edite la sintaxis de mis ejemplos. He escogido los ejemplos muy específicamente para demostrar las características del lenguaje.

    –James McNellis

    4 de marzo de 2012 a las 5:58

  • Como nota al margen, el estándar C establece explícitamente que una combinación de &* se anulan entre sí (6.5.3.2): "The unary & operator yields the address of its operand." /–/ "If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.".

    – Lundin

    30 de noviembre de 2015 a las 15:21


Creo que también es útil recordar que C es solo una abstracción para la máquina subyacente y este es uno de los lugares donde se está filtrando esa abstracción.

Desde la perspectiva de la computadora, una función es solo una dirección de memoria que, si se ejecuta, realiza otras instrucciones. Entonces, una función en C se modela como una dirección, lo que probablemente lleva al diseño de que una función es “la misma” que la dirección a la que apunta.

1647555488 754 ¿Por que las definiciones de puntero de funcion funcionan con
lewis kelsey

& y * son operaciones idempotentes sobre un símbolo declarado como función en C lo que significa func == *func == &func == *&func y por lo tanto *func == **funcpero tienen diferentes tipos, por lo que recibirá una advertencia.

El tipo de parámetro de una dirección de función pasada a una función puede ser int () o int (*)()y se puede pasar como *func, func o &func. Vocación (&func)() es lo mismo que func() o (*func)(). Enlace de Godbolt.

* y & no tienen significado en un símbolo de función, y en lugar de producir un error, el compilador elige interpretarlo como la dirección de func en ambos casos. La función no existe como un puntero separado, como un símbolo de matriz, por lo tanto &arr es lo mismo que arr, porque no es un puntero físico con una dirección en tiempo de ejecución, es un puntero lógico a nivel de compilador. es más *func leería el primer byte del código de función, que es una sección de código, y en lugar de producir un error del compilador o permitir que sea una falla de segmentación de error de tiempo de ejecución, el compilador simplemente lo interpreta como la dirección de la función.

& en un símbolo declarado como puntero de función obtendrá la dirección del puntero (porque ahora es una variable de puntero real que se manifiesta en la pila o sección de datos), mientras que funcp y *funcp aún se interpretará como la dirección de la función.

al llamar foo desde un puntero, incluso los paréntesis y el asterisco se pueden omitir, al igual que llamando directamente a la función con su nombre original, es decir (*p1_foo)() es equivalente a pi_foo().

Si todavía no está muy convencido con la respuesta de @JamesMcNellis, aquí tiene una prueba. Este es el AST (árbol de sintaxis abstracta) del compilador Clang. El árbol de sintaxis abstracta es la representación interna de la estructura del programa dentro del compilador.

void func1() {};
void test() {
    func1();
    (*func1)();
    (&func1)();

    void(*func1ptr)(void) = func1;
    func1ptr();
    (*func1ptr)();
    //(&func1ptr)();//error since func1ptr is a variable, &func1ptr is its address which is not callable.
}

AST:

//func1();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert func1 to pointer
|   `-DeclRefExpr //reference func1

//(*func1)();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert the funtion to pointer
|   `-ParenExpr //parentheses
|     `-UnaryOperator //* operator get function from the pointer
|       `-ImplicitCastExpr //implicitly convert func1 to pointer
|         `-DeclRefExpr //reference func1

//(&func1)();
|-CallExpr //call the pointer
| `-ParenExpr //parentheses
|   `-UnaryOperator //& get pointer from func1
|     `-DeclRefExpr //reference func1

//void(*func1ptr)(void) = func1;
|-DeclStmt //define variable func1ptr
| `-VarDecl //define variable func1ptr
|   `-ImplicitCastExpr //implicitly convert func1 to pointer
|     `-DeclRefExpr  //reference func1

//func1ptr();
|-CallExpr  //call the pointer
| `-ImplicitCastExpr //implicitly convert func1ptr to pointer
|   `-DeclRefExpr //reference the variable func1ptr

//(*func1ptr)();
`-CallExpr //call the pointer 
  `-ImplicitCastExpr //implicitly convert the function to pointer
    `-ParenExpr //parentheses
      `-UnaryOperator //* get the function from the pointer
        `-ImplicitCastExpr //implicitly convert func1ptr to pointer
          `-DeclRefExpr //reference the variable func1ptr

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad