Указатель функции typedef?


460

Я учусь динамически загружать DLL, но я не понимаю, что это за строка

typedef void (*FunctionFunc)();

У меня есть несколько вопросов. Если кто-то сможет ответить на них, я был бы благодарен.

  1. Почему typedefиспользуется?
  2. Синтаксис выглядит странно; после не voidдолжно быть имени функции или чего-то еще? Это похоже на анонимную функцию.
  3. Создан ли указатель на функцию для хранения адреса памяти функции?

Так что я в замешательстве в данный момент; Вы можете уточнить вещи для меня?


5
Взгляните на ссылку (последний раздел) learncpp.com/cpp-tutorial/78-function-pointers
энтузиастов

9
Следует отметить, что, поскольку с ++ 11 using FunctionFunc = void (*)();можно использовать вместо. Чуть более понятно, что вы просто объявляете имя для типа (указатель на функцию)
user362515

просто чтобы добавить к @ user362515, немного более понятная форма для меня:using FunctionFunc = void(void);
topspin

@topspin IIRC это не одно и то же. Один тип указателя на функцию, другой тип функции. Существует неявное преобразование, поэтому оно работает, IANA (C ++) L, так что можно вмешаться и исправить меня. В любом случае, если целью является определение типа указателя, я думаю, что синтаксис с *немного более явным.
user362515

Ответы:


463

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

  typedef int myinteger;
  typedef char *mystring;
  typedef void (*myfunc)();

используя их как

  myinteger i;   // is equivalent to    int i;
  mystring s;    // is the same as      char *s;
  myfunc f;      // compile equally as  void (*f)();

Как видите, вы можете просто заменить имя typedefed определением, приведенным выше.

Сложность заключается в указателе на синтаксис функций и читаемость в C и C ++, и они typedefмогут улучшить читаемость таких объявлений. Однако синтаксис является подходящим, поскольку функции - в отличие от других более простых типов - могут иметь возвращаемое значение и параметры, таким образом, иногда длинное и сложное объявление указателя на функцию.

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

Чтобы ответить на ваши три вопроса

  • Почему используется typedef? Для облегчения чтения кода - особенно для указателей на функции или структурные имена.

  • Синтаксис выглядит странно (в указателе на объявление функции) Этот синтаксис не очевиден для чтения, по крайней мере, в начале. Использование typedefдекларации вместо этого облегчает чтение

  • Создан ли указатель на функцию для хранения адреса памяти функции? Да, указатель функции хранит адрес функции. Это не имеет ничего общего с typedefконструкцией, которая только облегчает написание / чтение программы; компилятор просто расширяет определение typedef перед компиляцией фактического кода.

Пример:

typedef int (*t_somefunc)(int,int);

int product(int u, int v) {
  return u*v;
}

t_somefunc afunc = &product;
...
int x2 = (*afunc)(123, 456); // call product() to calculate 123*456

6
в последнем примере, просто «квадрат» не ссылается на то же самое, то есть указатель на функцию вместо использования & квадрат.
Пранавк

2
Вопрос, в первом примере ЬурейеГо у вас есть формы , typedef type aliasно с указателями на функции там только кажется , 2 аргумента, typedef type. По умолчанию псевдоним - имя, указанное в аргументе типа?
дчхетри

2
@pranavk: Да, squareи &square(и, действительно, *squareи **square) все ссылаются на один и тот же указатель на функцию.
Джонатан Леффлер

6
@ user814628: Не совсем понятно, о чем ты спрашиваешь. С typedef int newname, вы делаете newnameпсевдоним для int. С помощью typedef int (*func)(int)вы создаете funcпсевдоним для int (*)(int)- указатель на функцию, принимающую intаргумент и возвращающую intзначение.
Джонатан Леффлер

10
Я думаю, я просто запутался в заказе. С typedef int (*func)(int)я понимаю, что func - это псевдоним, немного запутанный, потому что псевдоним запутан с типом. В typedef int INTкачестве примера мне было бы легче, если бы указатель на функцию typedef имел форму typedef int(*function)(int) FUNC_1. Таким образом, я могу видеть тип и псевдоним в двух отдельных токенах, а не объединяться в один.
дчхетри

188
  1. typedefиспользуется для псевдонимов типов; в этом случае вы альясинг FunctionFuncк void(*)().

  2. Действительно, синтаксис выглядит странно, посмотрите на это:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
  3. Нет, это просто говорит компилятору, что FunctionFuncтип будет указателем на функцию, он не определяет его, например:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"

27
typedefвовсе не объявить новый тип. у вас может быть много typedefопределенных имен одного и того же типа, и они не различаются (например, в случае перегрузки функций). Существуют некоторые обстоятельства, при которых в отношении того, как вы можете использовать имя, typedefопределенное имя не совсем эквивалентно тому, как оно определено, но несколько typedefопределенных имен для одного и того же эквивалентны.
ура и hth. - Альф

Ах, я понял это сейчас. Спасибо.
Джек Харвин

4
+1 за ответ на вопрос 2 - очень понятно. Остальные ответы тоже были ясны, но этот выделяется для меня :-)
Michael van der Westhuizen

33

Без typedefслова, в C ++ объявление объявило бы переменную FunctionFuncтипа указатель на функцию без аргументов, возвращая void.

С typedefвместо этого он определяет FunctionFuncкак имя для этого типа.


5
Это действительно хороший способ думать об этом. Если вы внимательно прочитаете другие ответы, они на самом деле не решают путаницу ОП относительно запутывания псевдонима и имени. Изучив что-то новое, прочитав этот пост.
Безумный физик

9

Если вы можете использовать C ++ 11, вы можете использовать std::functionи usingключевое слово.

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;

1
без usingключевого слова C ++ 11 будет похоже typedef std::function<void(int, std::string)> FunctionFunc;на случай, если кто-то захочет использовать другую оболочку для функций без C ++ 11
Top-Master

2
#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}

1
При каких обстоятельствах требуется «&»? Например, одинаково ли хорошо работают func_p = & findDistance и func_p = findDistance?
Донна

1
Имя функции является указателем, поэтому вам не нужно использовать «&» с ним. Другими словами, «&» является необязательным, когда рассматриваются указатели на функции. Однако для примитивного типа данных вам нужно использовать '&', чтобы получить его адрес.
Amjad

1
Просто всегда казалось странным, что '&' может быть необязательным. Если typedef используется для создания указателя на примитив (т. typedef (*double) p
Донна

Я думаю, что вы хотели напечатать, typedef double* pчтобы определить указатель на двойной. Если вы хотите заполнить pуказатель из простой переменной, вам нужно использовать '&'. Примечание: мы * <имя указателя> разыменуем указатель. *Используются в указателе функции разыменования указателя (имя функции) и перейти к блоку памяти , где находится инструкция функции.
Amjad

2

Для общего случая синтаксиса вы можете посмотреть в приложении A стандарта ANSI C .

В форме Бэкуса-Наура оттуда вы можете видеть, что typedefимеет тип storage-class-specifier.

В типе declaration-specifiersвы можете видеть, что вы можете смешивать множество типов спецификаторов, порядок которых не имеет значения.

Например, правильно сказать,

long typedef long a;

определить тип aкак псевдоним для long long. Итак, чтобы понять typedef для исчерпывающего использования, вам нужно обратиться к некоторой форме backus-naur, которая определяет синтаксис (есть много правильных грамматик для ANSI C, не только для ISO).

Когда вы используете typedef для определения псевдонима для типа функции, вам нужно поместить псевдоним в то же место, где вы поместили идентификатор функции. В вашем случае вы определяете тип FunctionFuncкак псевдоним для указателя на функцию, чья проверка типа отключена при вызове и ничего не возвращает.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.