2016-11-10 3 views
1

В одном из ответов на вопрос How do function pointers in C work? один пользователь объясняет, как использовать указатели функций в возвращаемых значениях. Вот код, о котором идет речь:Возврат указателя на функцию в синтаксисе Си

// this is a function called functionFactory which receives parameter n 
// and returns a pointer to another function which receives two ints 
// and it returns another int 

int (*functionFactory(int n))(int, int) { 
    printf("Got parameter %d", n); 
    int (*functionPtr)(int,int) = &addInt; 
    return functionPtr; 
} 

Для меня так, как вы объявляете функцию functionFactory очень странно - мне кажется, что вы перепутать тип возврата (указатель на функцию) и само имя функции (functionFactory).

Например, когда мы пишем простую функцию, которая возвращает квадрат аргумента мы пишем что-то вроде

int square(int n){ 
    return n*n; 
} 

Очевидно, типа того, что мы возвращаемся на левом, а затем записать имя функции а затем какие параметры он принимает. Поэтому, когда мы возвращаем указатель на функцию, почему бы нам не написать что-то вроде:

(int (*function)(int, int)) functionFactory(int n) { ... 

Здесь Возвращаемый тип (который является указателем на функцию) и ее детали (например, что функция, которую мы указываем возвращает и то, что он принимает в качестве параметров) явно слева, а имя функции functionFactory - справа. Мне моя версия кажется гораздо более логичной и понятной, почему бы нам не написать ее так?

+1

C использует инфиксную нотацию для деклараторов. Это соответствует принципу «заявление следует за использованием». Мы получаем массив как 'a [5]', поэтому он объявлен как 'int a [5];', а не 'int [5] a;'. То же самое происходит с типами функций. –

+0

'int (* function) (int, int) functionFactory (int n) {...' "* Здесь тип возврата (который является указателем на функцию) *" - так что же самое первое 'int' в том, что line, если не тип возврата? – TessellatingHeckler

+0

Вы можете, конечно, объявить typedefs для этих указателей функций, а затем объявления вашей функции с помощью этих typedef будут выглядеть более «нормальными». – paddy

ответ

1

Следующий код объявляет переменную с именем func быть указателем на функцию, которая возвращает int и принимает два int S в качестве параметров:

int (*func)(int, int); 

Этот синтаксис имитирует синтаксис, как функции объявлены в C:

int func(int, int); 

Посмотрите, как это похоже? На самом деле функции в C аналогичны переменным, указывающим на функции, например. см этого примера:

int funcA(int a, int b) { ... } 
int (*funcB)(int, int) = funcA; 

Это не играет никакой роли по остальной части коды, если вы звоните funcA(a,b) или funcB(a,b), делает это? Так что хотя funcB является переменной и funcA функция, правда, есть функция где-то в памяти, а funcA и funcB оба указателя на адрес функционального кода.

Если вы хотите, чтобы придираться, funcA является константой и funcB является переменной, поэтому выход компилятор для вызова funcA будет отличаться для funcB; также в одном случае это прямой вызов, а в одном случае - косвенный вызов, поскольку пункт назначения funcB может меняться со временем. Это также означает, что звездочка говорит: «Я не функция, я - указатель на функцию».

Важно отметить, что в C звездочка всегда рядом с именем переменной, чтобы отметить переменную-указатель, так как еще вы хотели бы написать ее? Как это?

// Careful! Wrong! 
int (int, int) *func; 

Возможно, это более читаемо для вас? Или может быть так?

// Careful! Wrong! 
int()(int, int) *func; 

Этот синтаксис также не очень ясен, и он не имитирует никакой другой существующий синтаксис Си.Если вы много работаете с указателями на функции, передать их вокруг и хранить их в переменных, вы должны объявить собственный тип для указателя функции:

#include <stdio.h> 

int sum (int a, int b) { 
    return (a + b); 
} 

typedef int (* MathFuncType)(int, int); 

MathFuncType getSumFunc () { 
    return &sum; 
} 

int main(void) { 
    MathFuncType func = getSumFunc(); 
    printf("%d\n", func(3, 8)); 
    return 0; 
} 

http://rextester.com/GEKR20461

2

Это выпадает как описатель синтаксиса работ. Это может помочь подумать о замене. Вот один из способов, чтобы посмотреть его:

T f (); // f is a function returning T 
    | 
    v 
T (*p) (); // p is a pointer to a function returning T 
    | 
    v 
T (*q())(); // q is a function returning a pointer to a function returning T 

Итак, мы начинаем с функцией описателя f(). Затем мы заменяем f указателем (*p), предоставляя нам декларатор (*p)(). Наконец, мы заменяем p на функцию q(), предоставляя нам декларатор (*q())().

Редактировать

Вы можете слышать, как люди говорят о «спирали правило» при чтении волосатых declarators. Хотя это больше руководства, чем фактическое правило, выпадает из следующих правил старшинства:

T *a[N]; // a is an array of pointer to T 
T (*a)[N]; // a is a pointer to an array of T 
T *f();  // f is a function returning T 
T (*f)(); // f is a pointer to a function returning T 

Постфиксной [] и () операторы имеют более высокий приоритет, чем унарный *, следовательно, потребность в скобки при объявлении указателей на массивы или функций. Поэтому, когда мы читаем T (*q())();, мы начинаем с самого левого идентификатора q и «спираль» наружу:

+-----------+ 
    | +-------+ | 
    | | +---+ | |   
    | | | | | | 
    T (* q())() 
     | | | | | | 
     | | +-+ | | 
     | +-----+ |  
     +---------+ 

разбив его по частям:

q  -- q is a 
    q()  -- function returning 
    *q()  -- pointer to 
    (*q())() -- function returning 
T (*q())(); -- T 

Вы не может объявить массив типа функции, и вы не можете объявлять функции для возврата типов массивов:

T a[N](); // NOT ALLOWED 
T f()[N]; // NOT ALLOWED 

так в любой комбинации o f и типы массивов, всегда будет задействован указатель, поэтому выполняется правило спирали.

Смежные вопросы