Как typedef работает для указателей на функции - PullRequest
10 голосов
/ 20 февраля 2012

Я думаю, что я могу страдать от страшной болезни "случайного программиста", по крайней мере, когда речь идет о typedefs и указателях функций.Поэтому я экспериментировал со всеми видами комбинаций, включающих их, чтобы проанализировать результаты на основе всех полученных результатов.

Но поскольку я продолжал пробовать разные комбинации, вместо того, чтобы анализировать результаты, я сейчас просто потерян в процессе.

Я надеюсь, что вы, ребята, поможете мне разобраться в этом беспорядке.

Первый пример кода

typedef void (print)(void);
void do_something (void) { printf("Hello World\n"); }

print *pr;
pr = &do_something;
pr(); // Hello World

Второй пример кода

typedef void (print)(void);
void do_something (void) { printf("Hello World\n"); }

print *pr;
pr = do_something;
pr(); // Hello World

Как работают оба приведенных выше примера кода, как будто '&' не влияет на имя функции

третий пример кода

typedef void (print)(void);
void do_something (void) { printf("Hello World\n"); }

print pr;
pr = do_something; // compile error
pr = &do_something; // compile error
pr();

Я надеялся, что одно из вышеперечисленных заданий будет работать здесь, но, черт возьми!Я действительно не понимаю указатели на функции (и, возможно, также typedef).

Ответы [ 4 ]

19 голосов
/ 20 февраля 2012

Адрес имени функции и простое имя функции означают одно и то же, поэтому & не влияет на имя функции.

Аналогично, при использовании указателей на функции множественная разыменование не является проблемой:

#include <stdio.h>
typedef void print(void);
static void dosomething(void) { printf("Hello World\n"); }

int main(void)
{
    print *f1 = dosomething;
    print *f2 = &dosomething;
    f2();
    (f1)();
    (*f1)();
    (**f2)();
    (***f1)();
    (****f2)();
    (*****f1)();
}

Это компилируется чисто в:

gcc -O3 -g -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition -std=c99 xx.c -o xx

Я бы не стал утверждать, что несколько звезд - это хороший стиль; это не так Это «странно и (да, вы можете сказать это) извращенно». Одного достаточно (и одна звездочка в основном предназначена для таких людей, как я, которые научились программировать на C до того, как стандарт сказал: «Можно вызывать функцию через указатель без использования записи (*pointer_to_function)(arg1, arg2); вы можете просто написать pointer_to_function(arg1, arg2) если хочешь"). Да, это странно. Нет, ни один другой тип (или класс типов) не проявляет такого же поведения, слава богу.

7 голосов
/ 20 февраля 2012

Особенность указателей на функции заключается в том, что они являются указателями на функции ! :-) Вот как вы можете заставить работать свой третий пример:

#include <stdio.h>
typedef void (*print)(void);
//            ^
void do_something (void) { printf("Hello World\n"); }
int main (void) {
    print pr;
    pr = do_something; // &do_something would also work.
    pr();
    return 0;
}

С точки зрения того, используете ли вы funcName или &funcName, это не имеет значения (по крайней мере, в C). Раздел 6.3.2.1 Lvalues, arrays and function designators гласит:

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

3 голосов
/ 20 февраля 2012

Как и C, C ++ имеет указатель на функции: void (*)() Например, это указатель на функцию, которая не принимает аргументов и не возвращает значений. Однако в C ++ также введены ссылки на функции void (&)(), и между ними существуют неявные преобразования (хотя я точно не помню правила).

Таким образом:

  • funcname является ссылкой на функцию
  • &funcname - указатель на функцию

Обратите внимание, что для получения адреса (или ссылки на) перегруженной функции требуется static_cast для точного типа (для устранения перегрузки).

3 голосов
/ 20 февраля 2012

Оказывается, что в C / C ++ оба значения funcname и &funcname дадут адрес funcname и могут быть назначены переменной указателя функции. На самом деле это просто странность того, как был разработан синтаксис для языка (ов).

...