В этом есть несколько частей, которые позволяют всем этим комбинациям операторов работать одинаково.
Основная причина, по которой все эти работы заключаются в том, что функция (например foo
) неявно преобразуется в указатель на функцию. Вот почему void (*p1_foo)() = foo;
работает: foo
неявно преобразуется в указатель на себя и этот указатель назначается p1_foo
.
Унарный &
, при применении к функции, выдает указатель на функцию, точно так же, как и адрес объекта, когда он применяется к объекту. Для указателей на обычные функции он всегда избыточен из-за неявного преобразования функции в указатель на функцию. В любом случае именно поэтому и void (*p3_foo)() = &foo;
работает.
Унарный *
, при применении к указателю на функцию, возвращает указанную функцию, точно так же, как и на указательный объект, когда он применяется к обычному указателю на объект.
Эти правила могут быть объединены. Рассмотрим ваш второй до последнего примера **foo
:
- Во-первых,
foo
неявно преобразуется в указатель на себя, а первый *
применяется к этому указателю функции, foo
снова получая функцию .
- Затем результат снова неявно преобразуется в указатель на себя, и применяется второе
*
, снова получая функцию foo
.
- Затем он снова неявно преобразуется в указатель функции и присваивается переменной.
Вы можете добавить столько *
s, сколько захотите, результат всегда одинаков. Чем больше *
с, тем лучше.
Мы также можем рассмотреть ваш пятый пример &*foo
:
- Во-первых,
foo
неявно преобразуется в указатель на себя; применяется унарный *
, foo
снова уступающий .
- Затем
&
применяется к foo
, давая указатель на foo
, который присваивается переменной.
&
Может быть применен только к функции , хотя, а не к функции , которая была преобразована в указатель функции (если, конечно, указатель функция не является переменной, и в этом случае результатом является указатель на а-pointer- к функции, например, вы можете добавить в свой список void (**pp_foo)() = &p7_foo;
).
Вот почему &&foo
не работает: &foo
это не функция; это указатель на функцию, который является rvalue. Однако &*&*&*&*&*&*foo
будет работать, как и &******&foo
потому, что в обоих этих выражениях &
всегда применяется к функции, а не к указателю функции rvalue.
Также обратите внимание, что вам не нужно использовать унарный код *
для вызова через указатель функции; оба (*p1_foo)();
и (p1_foo)();
имеют одинаковый результат, опять же из-за преобразования функции в указатель функции.
&foo
принимает адресfoo
, в результате чего указатель на функцию указываетfoo
, как и следовало ожидать.&
операторы для объектов: данныйint p;
,&p
возвращает указатель наp
и является выражением rvalue;&
оператор требует именующего выражения.*
, тем меньше веселья .&*
взаимно компенсирует друг друга (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."
.Я думаю, также полезно помнить, что C - это просто абстракция для базовой машины, и это одно из мест, где эта абстракция протекает.
С точки зрения компьютера, функция - это просто адрес памяти, который, если выполняется, выполняет другие инструкции. Таким образом, функция в C сама моделируется как адрес, что, вероятно, приводит к тому, что функция «совпадает» с адресом, на который она указывает.
источник
&
и*
являются идемпотентными операциями над символом, объявленным как функция в C, что означаетfunc == *func == &func == *&func
и, следовательно,*func == **func
Это означает, что тип
int ()
совпадаетint (*)()
с параметром функции, и определенный функционал может быть передан как*func
,func
или&func
.(&func)()
так же, какfunc()
. Годболт ссылка.Функция действительно адрес, поэтому
*
и&
не имеет никакого смысла, и вместо того , чтобы выдавать ошибку, то выбирает компилятор , чтобы интерпретировать его как адрес FUNC.&
на символ, объявленный как указатель на функцию, однако получит адрес указателя (потому что теперь он имеет отдельное назначение), тогда какfuncp
и*funcp
будет идентичнымисточник