«Извените за многа букаф»
Murav писал(а):Имя функции - это только имя функции и указателем оно само по себе не является. Как раз более правильная запись получения указателя на функцию - использование &. Кстати вызывать функцию через указатель, в отличие от имени, можно с разыменовыванием (*funcptr)() , но естественно никто так не пишет.
На мой взгляд, всё-таки имя функции ближе к имени массива - само по себе уже является указателем. И никакого "в отичие от имени".
Немного цитат из стандарта С99 (в С89 то же самое, но в 99 местами подробнее расписано).
Код: Выделить всё
6.9.1 Function definitions
...
EXAMPLE 2
To pass one function to another, one might say
int f(void);
/* ... */
g(f);
Then the definition of g might read
void g(int (*funcp)(void))
{
/* ... */
(*funcp)() /* or funcp() ... */
}
or, equivalently,
void g(int func(void))
{
/* ... */
func() /* or (*func)() ... */
}
Обратите внимание - в примере из стандарта вызове функции g(f) нет взятия адреса &, в этом нет смысла (хотя и можно сделать), так как:
6.3.2 Other operands
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 operator 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 попадает не указатель, у которого есть размер, а «непонятно что наравне с неполным типом» и получается ошибка времени компиляции). Т.е. фактически везде, где применяется имяфункции - оно рассматривается как указатель на неё (ещё об этом ниже в цитате из 6.5.2.2 Function calls).
Оба определения функции g() эквивалентны. Во втором в качестве параметра выступает не указатель на функцию, а
функция (как и в случае массива - параметром можно указать массив и в теле обращаться как к массиву, реально передаётся указатель и для функции тоже). При этом во втром варианте, точно так же, как и в первом, можно использовать оба варианта вызова - и просто с именем, и с разименованием.
Собственно,
Код: Выделить всё
#include <avr/io.h>
uint8_t foo(uint8_t a);
void moo()
{
PORTB = foo(1);
PORTB = (*foo)(2);
}
/usr/bin/avr-gcc
-std=c89 --pedantic -Os -S -mmcu=atmega8 funcptr.c
Код: Выделить всё
.global moo
.type moo, @function
moo:
ldi r24,lo8(1)
rcall foo
out 56-32,r24
ldi r24,lo8(2)
rcall foo
out 56-32,r24
ret
Ещё
6.5.2.2 Function calls
Constraints
1 The expression that denotes the called function 77) shall have type pointer to function returning void or returning an object type other than an array type.
...
Semantics
3 A postfix expression followed by parentheses () containing a possibly empty, comma-separated list of expressions is a function call. The postfix expression denotes the called function. The list of expressions specifies the arguments to the function.
...
77) Most often, this is the result of converting an identifier that is a function designator.
Т.е. любой вызов функции - это работа с указателем на функцию, который, согласно сноске 77, "чаще всего получается в качестве преобразования идентификатора, являющегося обозначением функции"
И вообще, этот параграф 6.5.2.2 есть часть раздела
6.5.2 Postfix operators
Syntax
postfix-expression:
primary-expression
postfix-expression [ expression ]
postfix-expression ( argument-expression-list opt )
postfix-expression . identifier
postfix-expression -> identifier
postfix-expression ++
postfix-expression --
( type-name ) { initializer-list }
( type-name ) { initializer-list , }
Выделенное - это и есть вызов функции. Т.е. любой вызов функции - это выражение с постфиксным оператором
( ), внутри которого могут опционально быть аргументы функции.
Кстати, довольно свободное обращение с именами функций было уже в Алголе-68 (да, да, я часто это цитирую, но оно мне нравится)
в C-шном эквиваленте
Ну и совсем ради хохмы
#include <avr/io.h>
uint8_t foo(uint8_t a);
void moo()
{
PORTB = (****foo)(2);
}
Точно так же прекрасно компилируется в простой вызов. Уже давно под рукой ничего, кроме gcc нет (хотя с --pedantic он ругается на несоответсвия стандарту), но это как бы не в Borland C 3.1 тоже компилировалось. Я это понимаю так
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; ...
foo - function designator
согласно 6.3.2.1 моментально превращается в pointer to function
согласно 6.5.3.2 при помощи самой правой звёздочки превращается назад в function designator
но стоит в выражении и согласно 6.3.2.1 ... далее по тексту, но уже со следующей звёздочкой.
p.s. антиоффтоп: согласен с ARV по поводу того, что раз уж захотелось в структуре указателей на функции в таком количестве, то стоит подумать о явном приенении С++ вместо его ручной эмуляции.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.