Страницы

Поиск по вопросам

вторник, 30 октября 2018 г.

Синтаксис вызова функции указателем

Меня мучает вопрос, с какой целью это сделано?
Есть такой указатель: int (*a)() = ownfucntion; /// ownfucntion - любая пользовательская функция.
Мы можем эту функцию вызвать так: a();
А можем так: (*a)();
Где логика? Обычно разименовывание используется для получения значения из указателя. А тут результат одинаков: хочешь разименовывай, хочешь не разименовывай в итоге будет вызвана функция.


Ответ

Обозначение функции всегда за исключением одного случая преобразуется в указатель на функцию.
Из стандарта C (6.3.2.1 Lvalues, arrays, and function designators)
4 A function designator is an expression that has function type. Except when it is the operand of the sizeof operator65) or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
Хотя в этой цитате написано про использование оператора sizeof, но на самом деле функция не может использоваться в операторе sizeof
Стандарт C (6.5.3.4 The sizeof and alignof operators)
1 The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an expression that designates a bit-field member
Поэтому когда используется постфиксное выражение вызова функции, то обозначение функции неявно преобразуется к указателю на функцию (6.5.2.2 Function calls)
1 The expression that denotes the called function92) shall have type pointer to function returning void or returning a complete object type other than an array type.
В связи с этим можно написать даже такое выражение, как показано в следующей демонстрационной программе
#include
void f( void ) { puts( "Oh...At last I am called!" ); }
int main(void) { ( **********f)();
return 0; }
Вывод программы на консоль:
Oh...At last I am called!
Потому что согласно стандарту C (6.5.3.2 Address and indirection operators)
4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.102
То есть сначала обозначение функции преобразуется неявно к указателю на функцию. Затем после применения оператора * этот указатель преобразуется к обозначению функции и сразу же снова преобразуется в указатель на функцию и т.д.
Вы можете применить столько операторов * к обозначению функцию, сколько позволит конкретный компилятор

Подытоживая, можно сказать следующее. Для объявления функций вводится их идентификатор, чтобы по нему можно было ссылаться на функции и отличать одну функцию от другой. При этом сама функция может быть еще не определена, а лишь только объявлена.
Когда этот идентификатор функции или, другими словами, обозначение функции используется в выражениях, то оно преобразуется к типу указателя на функцию.
В выражениях используются значения. Какое значение назначить обозначению функции, используемому в выражениях?
Естественно разумно в качестве значения использовать адрес функции, потому что все, для чего создаются функции, это для их выполнения. А выполнить функцию можно, лишь передав управление по тому адресу, где эта функция определена.
Поэтому в качестве значения функции и принят ее адрес, то есть функция в выражениях преобразуется к указателю на саму себя.
Единственным исключением по отношению к неявному преобразования функции к указателю на функцию является использование оператора &. Здесь нет необходимости преобразовывать функцию неявно к указателю на нее, так как применение оператора & итак является явным взятием адреса функции.

Комментариев нет:

Отправить комментарий