Почему определения указателей на функции работают с любым количеством амперсандов '&' или звездочек '*'? - PullRequest
200 голосов
/ 01 августа 2011

Почему следующие работы?

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)();
}

Ответы [ 2 ]

207 голосов
/ 01 августа 2011

Есть несколько вещей, которые позволяют всем этим комбинациям операторов работать одинаково.

Основная причина, по которой все эти работы заключаются в том, что функция (например, foo) неявно преобразуется в указатель на функцию. Вот почему void (*p1_foo)() = foo; работает: foo неявно преобразуется в указатель на себя, и этот указатель присваивается p1_foo.

Унарный &, когда применяется к функции, выдает указатель на функцию, так же, как и адрес объекта, когда он применяется к объекту. Для указателей на обычные функции он всегда избыточен из-за неявного преобразования функции в указатель на функцию. В любом случае, именно поэтому void (*p3_foo)() = &foo; работает.

Унарный * при применении к указателю на функцию дает указанную функцию, так же, как и на указатель на объект, когда он применяется к обычному указателю на объект.

Эти правила можно комбинировать. Рассмотрим пример с второго по последний, **foo:

  • Во-первых, foo неявно преобразуется в указатель на себя, а первый * применяется к этому указателю функции, снова получая функцию foo.
  • Затем результат снова неявно преобразуется в указатель на себя, и применяется второй *, снова получая функцию foo.
  • Затем он снова неявно преобразуется в указатель функции и присваивается переменной.

Вы можете добавить столько *, сколько пожелаете, результат всегда одинаков. Чем больше * с, тем лучше.

Мы также можем рассмотреть ваш пятый пример, &*foo:

  • Во-первых, foo неявно преобразуется в указатель на себя; применяется унарный *, снова получая foo.
  • Затем & применяется к foo, давая указатель на foo, который присваивается переменной.

& может применяться только к функции, но не к функции, которая была преобразована в указатель функции (если, конечно, указатель функции не является переменной, и в этом случае результатом является указатель- to-a-pointer-to-a-function, например, вы можете добавить в свой список void (**pp_foo)() = &p7_foo;).

Вот почему &&foo не работает: &foo не является функцией; это указатель на функцию, являющийся значением. Однако &*&*&*&*&*&*foo будет работать, как и &******&foo, потому что в обоих этих выражениях & всегда применяется к функции, а не к указателю на функцию rvalue.

Обратите внимание, что вам не нужно использовать унарный *, чтобы сделать вызов через указатель функции; (*p1_foo)(); и (p1_foo)(); имеют одинаковый результат, опять же из-за преобразования указателя функции в функцию.

5 голосов
/ 09 марта 2016

Я думаю, также полезно помнить, что C - это просто абстракция для базовой машины, и это одно из мест, где эта абстракция протекает.

С точки зрения компьютера, функция - это просто адрес памяти, который при выполнении выполняет другие инструкции. Таким образом, функция в С сама моделируется как адрес, что, вероятно, приводит к тому, что функция «совпадает» с адресом, на который она указывает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...