1

Тема: C: Вказівник на функцію. void (*func)()

Знайшов на стековерфлов питання:
"Чому вказівник на функцію працює з будь-якою кількістю зірочок '*' ?"

void function1(void (*func)())
{
    (*func)(); // (*func) - сама функція.
    func();    // func - вказівник на функцію.

    (*&func)(); // = func();
    (&*func)(); // = func();

    (**&func)(); // = (*func)();
    (*&*func)(); // = (*func)();
    (&**func)(); // = func();

    (**func)();   // = (*func)();
    (***func)();  // = (*func)();
    (****func)(); // = (*func)();
}
void function2(void (**func)())
{
    (*func)(); // ok
    //func();  // error

    //(*&func)(); // error
    //(&*func)(); // error

    (**&func)(); // ok
    (*&*func)(); // ok
    (&**func)(); // ok

    (**func)();   // ok
    (***func)();  // ok
    (****func)(); // ok
}

Я так зрозумів, що будь-який вказівник на функцію при розадресації (*func) автоматично конвертується в той самий вказівник на функцію (func).
А при використанні вказівника як функції (func()), викликається сама функція ((*func)()).
Поправте мене, якщо ляпнув щось не те.

Тепер питання:
Всі ці перетворення вказівників відбуваються на етапі компіляції (сподіваюсь), чи під час виконання програми ?
І для "void (*func)()" як краще викликати функцію ? "(*func)();" чи "func();" ? (мається на увазі, чи не виникнуть в мене проблеми на старих компіляторах, якщо я буду використовувати якусь одну із цих форм).

2

Re: C: Вказівник на функцію. void (*func)()

Перетворення, очевидно, відбувається на етапі компіляції. void (*func)() зберігається у вигляді одного вказівника,  там нема чому під час виконання перетворюватися.

Третій варіант - залежно від ситуації. Якщо цей вказівник є членом класу і встановлюється деінде, а мені тільки треба викликати - то func(). Якщо ж я щойно перевстановлював вказівники на функції, то (*func)(), щоб не переплуталося.
Ніколи не чув про проблеми зі старими компіляторами на цю тему.

Подякували: leofun011

3

Re: C: Вказівник на функцію. void (*func)()

koala написав:

Якщо цей вказівник є членом класу і встановлюється деінде, а мені тільки треба викликати - то func(). Якщо ж я щойно перевстановлював вказівники на функції, то (*func)(), щоб не переплуталося.

Можна тут детальніше (на прикладі) ? Я не вловив різницю між цими двома випадками.. що там може переплутатись ?

4

Re: C: Вказівник на функцію. void (*func)()

Ну, не переплутатися, а просто некрасиво виглядатиме.
Перше:

class MyObject
{
  ...
  void (*clickHandler)(void);
  ...
}
void MyObject::someFunction()
{
  ...
  clickHandler(); //викликаємо обробник події
}

Друге:

clickHandler = &func1;
(*clickHandler)();//щойно було встановлено вказівник, якщо викликати його напряму, це викликає у мене збій сприйняття
Подякували: leofun011

5 Востаннє редагувалося leofun01 (09.07.2016 21:07:50)

Re: C: Вказівник на функцію. void (*func)()

О, класно.
Виникло ще одне питання по темі:
Якщо є функції

void f1(void (*func)()) { /* ... */ }
void f2() { /* ... */ }

Як краще передавати вказівник на f2 в f1 ?

f1(&f2);

чи

f1(f2);

?

6

Re: C: Вказівник на функцію. void (*func)()

А ось тут я все ж пишу f1(&f2) - щоб явно показати, що йде передача вказівника.

Подякували: leofun011

7 Востаннє редагувалося reverse2500 (09.07.2016 22:57:14)

Re: C: Вказівник на функцію. void (*func)()

пропоную варіант глянути все в дізассемблері, він все підкаже що як

- Поганому трояну фаєрвол заважає
- Ніколи не програмуйте та не пийте пиво
Якщо ви з першого разу написали програму, в якій немає жодної помилки, повідомте про це системного програмісту: він виправить помилки в компіляторі
Подякували: leofun011

8

Re: C: Вказівник на функцію. void (*func)()

leofun01 написав:

"Чому вказівник на функцію працює з будь-якою кількістю зірочок '*' ?"
Я так зрозумів, що будь-який вказівник на функцію при розадресації (*func) автоматично конвертується в той самий вказівник на функцію (func).
А при використанні вказівника як функції (func()), викликається сама функція ((*func)()).

Ну, при розадресації *func стає function designator. Але справа в тому, що function designator скрізь (крім sizeof та &) автоматично і моментально перетворюється у pointer to function. Тобто:

void foo(void);

void moo()
{
    foo(); // навіть тут foo розглядається як _вказівник_ на функцію.
}
стандарт C99 написав:

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 54) 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’’.

6.5.2.2 Function calls
1 The expression that denotes the called function 77) shall have type pointer to function …

3 A postfix expression followed by parentheses () …  is a function call.

77) Most often, this is the result of converting an identifier that is a function designator.

Процитоване я розумію як те, що у **func після першої зірочки не встигаємо ми кліпнути, а отриманий function designator знову у виразі перетворюється на pointer to function і так стільки разів, скільки зірочок. Звісно, все на етапі компіляції
Виходячи з цього, я вважаю, писати зірочки нема ні найменшої потреби.

leofun01 написав:

чи не виникнуть в мене проблеми на старих компіляторах, якщо я буду використовувати якусь одну із цих форм).

Починаючи з C89 поведінка цього діла не мінялася. За до-ansi-шні компілятори не скажу але, здається, було те ж саме. Принаймні речі на зразок

val = (a>0 ? sin : cos)(x);

компілювалися. Та й у С воно прийшло з Algol-68, де можна було написати val := (if a > 0 then sin else cos fi)(x)

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: leofun011

9

Re: C: Вказівник на функцію. void (*func)()

leofun01 написав:

Виникло ще одне питання по темі:
Якщо є функції

void f1(void (*func)()) { /* ... */ }
void f2() { /* ... */ }

Як краще передавати вказівник на f2 в f1 ?

f1(&f2);

чи

f1(f2);

?

Абсолютно все одно. Різниця лише на рівні «мені більше подобається такий вигляд» (я користуюся другим варіантом).
У першому випадкові отой designator не перетворюється автоматично у pointer (див вище про виключення з sizeof та &), а вказівником стає «вручну» завдяки &.
У другому випадкові — автоматичне перетворення.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: koala, leofun012