Покажчик функції Typedef?


458

Я вчуся як динамічно завантажувати DLL, але те, що я не розумію, це ця лінія

typedef void (*FunctionFunc)();

У мене є кілька питань. Якщо хтось зможе відповісти на них, я був би вдячний.

  1. Для чого typedefвикористовується?
  2. Синтаксис виглядає дивним; після не voidповинно бути імені функції чи чогось? Це схоже на анонімну функцію.
  3. Чи створений покажчик функції для зберігання адреси пам'яті функції?

Тож я зараз розгублений; чи можете ви мені прояснити речі?


5
Подивіться на посилання (останній розділ) learncpp.com/cpp-tutorial/78-function-pointers
enthusiasticgeek

9
Слід зазначити, що оскільки c ++ 11 using FunctionFunc = void (*)();можна використовувати замість цього. Трохи зрозуміліше, що ви просто оголошуєте ім'я типу (вказівник на функцію)
user362515

просто додати до @ user362515, трохи чіткіша форма для мене:using FunctionFunc = void(void);
topspin

@topspin IIRC ці два неоднакові. Один - тип функції вказівника, інший - тип функції. Існує неявна конверсія, тому вона працює, IANA (C ++) L, отже, можна вступити і виправити мене. У будь-якому випадку, якщо наміром є визначення типу вказівника, я думаю, що синтаксис з символом *є трохи більш явним.
користувач362515

Відповіді:


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)();

Як бачите, ви могли просто замінити набране ім'я набране слово на його визначення, яке було наведено вище.

Складність полягає у вказівці на синтаксис функцій та читабельність у 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
в останньому прикладі не просто «квадрат» посилається на те саме, тобто вказівник на функцію, а не & & квадрат.
pranavk

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 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визначені імена для одного і того ж є рівнозначними.
ура та хт. - Альф

Ах, я зрозумію це зараз. Дякую.
Джек Харвін

4
+1 для відповіді на питання 2 - дуже зрозуміло. Решта відповідей теж були зрозумілі, але ця для мене виділяється :-)
Майкл ван дер Вестхуйзен

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
Ім'я функції - вказівник, тому вам не потрібно використовувати "&" з нею. Іншими словами, "&" є необов'язковим, якщо враховуються покажчики функцій. Однак для примітивного типу даних вам потрібно використовувати "&", щоб отримати його адресу.
Амджад

1
Це завжди дивно, що "&" коли-небудь може бути необов'язковим. Якщо typedef використовується для створення вказівника на примітив (тобто typedef (*double) p, опція '&' є необов’язковою?
Donna

Я думаю, ви хотіли набрати, typedef double* pщоб визначити вказівник на подвійний. Якщо ви хочете заповнити pпокажчик з примітивної змінної, вам потрібно буде використовувати "&". Побічна примітка, ми вам * <ім'я вказівника>, щоб відкинути покажчик. *Використовуються в покажчику функції разименованія покажчика (ім'я функції) і перейти до блоку пам'яті , де знаходиться інструкція функції.
Амджад

2

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

У формі Backus-Naur звідти ви бачите, що 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.