Как понять это определение - PullRequest
12 голосов
/ 08 ноября 2010

В настоящее время я читал APUE. И я нашел функцию, определенную следующим образом:

void (*signal(int signo, void (*func)(int)))(int);

Я был сбит с толку, я знаю, что сигнал является указателем на функцию, а последний (int) является его параметром,я не знал, что это (int signo, void (* func) (int)).

Ответы [ 6 ]

24 голосов
/ 08 ноября 2010

Общая процедура: найдите самый левый идентификатор и проработайте свой выход.При отсутствии явной группировки с круглыми скобками постфиксные операторы, такие как () и [], связываются с унарными операторами, такими как *;таким образом, все верно:

T *x[N]             -- x is an N-element array of pointer to T
T (*x)[N]           -- x is a pointer to an N-element array of T
T *f()              -- f is a function returning a pointer to T
T (*f)()            -- f is a pointer to a function returning T

Применяя эти правила к объявлению, оно разбивается на

       signal                                      -- signal
       signal(                            )        -- is a function
       signal(    signo,                  )        -- with a parameter named signo 
       signal(int signo,                  )        --   of type int
       signal(int signo,        func      )        -- and a parameter named func
       signal(int signo,       *func      )        --   of type pointer
       signal(int signo,      (*func)(   ))        --   to a function
       signal(int signo,      (*func)(int))        --   taking an int parameter
       signal(int signo, void (*func)(int))        --   and returning void
      *signal(int signo, void (*func)(int))        -- returning a pointer
     (*signal(int signo, void (*func)(int)))(   )  -- to a function
     (*signal(int signo, void (*func)(int)))(int)  -- taking an int parameter
void (*signal(int signo, void (*func)(int)))(int); -- and returning void

Короче говоря, signal возвращает указатель на функцию, возвращающуюvoid.signal принимает два параметра: целое число и указатель на другую функцию, возвращающую void.

Вы можете использовать typedefs, чтобы упростить чтение (и справочная страница для signal в Ubuntu linux делает именно это);Тем не менее, я думаю, что полезно показать версию без определения типа, чтобы продемонстрировать, как именно работает синтаксис.Функция typedef замечательна, но вам действительно нужно понять, как работают базовые типы, чтобы эффективно ее использовать.

Функция signal устанавливает обработчик сигнала;второй аргумент - это функция, которая должна быть выполнена, если получен сигнал.Указатель на текущий обработчик сигнала (если есть) возвращается.

Например, если вы хотите, чтобы ваша программа обрабатывала сигналы прерывания (например, от Ctrl-C):

static int g_interruptFlag = 0;

void interruptHandler(int sig)
{
  g_interruptFlag = 1;
}

int main(void)
{
  ...
  /**
   * Install the interrupt handler, saving the previous interrupt handler
   */
  void (*oldInterruptHandler)(int) = signal(SIGINT, interruptHandler);

  while (!g_interruptFlag)
  {
    // do something interesting until someone hits Ctrl-C
  }

  /**
   * Restore the previous interrupt handler (not necessary for this particular
   * example, but there may be cases where you want to swap out signal handlers
   * after handling a specific condition)
   */
  signal(SIGINT, oldInterruptHandler);
  return 0;
}

EDIT Я расширил пример кода для signal к чему-то, что, надеюсь, более показательно.

16 голосов
/ 08 ноября 2010
void (*signal(int signo, void (*func)(int)))(int);

сигнал - это функция, которая принимает int и указатель на функцию, принимающую int и возвращающую void, и возвращает указатель функции, принимающий int и возвращающий void. То есть

typedef void(*funcPtr)(int)

тогда у нас есть

funcPtr signal(int signo, funcPtr func); //equivalent to the above

Синтаксис действительно странный, и такие вещи лучше делать с помощью typedef. Например, если вы хотите объявить функцию, которая принимает int и возвращает указатель на функцию, принимающую char и возвращающую double, будет

double (*f(int))(char);

Редактировать: после комментария, который гласит «Wooooooow», я приведу еще один пример, который является более «woooow»:)

Давайте объявим функцию, которая принимает
1. указатель на массив из 5 указателей на функции, каждая из которых принимает float и возвращает double.
2. указатель на массив из 3-х элементов на массивы из 4-х значений
и возвращает указатель на функцию, которая принимает указатель на функцию, принимая int и возвращая указатель на функцию, принимающую float и возвращающую void, и возвращает unsigned int.

Решение typedef будет следующим:

typedef double (*f1ptr) (float);
typedef f1ptr (*arr1ptr)[5];
typedef int (*arr2ptr)[4];
typedef arr2ptr (*arr3ptr)[3];
typedef void(*f2Ptr)(float);
typedef f2ptr (*f3ptr)(int);
typedef unsigned int (*f4ptr) (f3ptr);
f4ptr TheFunction(arr1ptr arg1, arr3ptr arg2);

Теперь забавная часть :) Без typedefs это будет :

 unsigned int (*TheFunction( double (*(*)[5])(float), int(*(*)[3])[4]))( void(*(*)(int))(float))

Боже мой, я только что написал это? :)

12 голосов
/ 08 ноября 2010

Правило «Спираль по часовой стрелке» поможет: http://c -faq.com / Децл / spiral.anderson.html

Существует три простых шага:

Начиная с неизвестного элемента, двигаться по спирали / по часовой стрелке; при обнаружении следующих элементов замените их соответствующими английскими выражениями:

[X] или [] => Размер массива X ... или Размер неопределенного массива ...

(type1, type2) => функция, передающая возвращение type1 и type2 ...

  • => указатель (и) на ...

Продолжайте делать это по спирали / по часовой стрелке, пока все жетоны не будут закрыты. Всегда сначала разрешите что-нибудь в скобках!

См. «Пример № 3:« Предельный »», который в значительной степени соответствует тому, что вы просите:

"сигнал - это функция, передающая int и указатель на функцию, передающую int, не возвращающую ничего (void), возвращающую указатель на функцию, возвращающую int, ничего не возвращающую (void)"

3 голосов
/ 08 ноября 2010

Если у вас нет доступа к cdecl прямо сейчас, вот вывод cdecl:

$ cdecl
cdecl> explain void (*signal(int , void (*)(int)))(int);
declare signal as function (int, pointer to function (int) returning void) returning pointer to function (int) returning void
1 голос
/ 08 ноября 2010

Этот сайт дает указания на C gibberish:

C gibberish <-> Английский

0 голосов
/ 08 ноября 2010

Установите cdecl для вашего дистрибутива (если доступно) или перейдите здесь

В противном случае, я считаю, что ответ Армена Цируняна верный.

...