Что означает этот кусок кода? void (* сигнал (int sig, void (* func) (int))) (int); - PullRequest
32 голосов
/ 14 сентября 2010

Я наткнулся на этот кусок кода и совершенно растерялся, интерпретируя его значение.

#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);

Каково подробное объяснение кода в строке 2?

Я знаю, что void и int - это типы, * func - указатель на функцию, а скобки - для приоритета.Но я все еще не получаю (* сигнал ...), (INT), и все это вместе взятые.Чем детальнее, тем лучше.

Возможно, я знал значение / эффект этой декларации.Но мне пришлось провести еще несколько испытаний, чтобы помочь мне понять, что происходит, как показано ниже:

  1 #include <signal.h>
  2 void (*signal)(int sig, void (*func)(int));
  3 void (*signal)(int);  // then void (signal)(int) again.
  4 //void (*signal(int sig, void (*func)(int)))(int); //break this line into two lines above
  5
  6 int main(){}

В приведенном выше коде я разбил void (*signal(int sig, void (*func)(int)))(int) на две строки.В строке 3 я попытался использовать void (*signal)(int) и void (signal)(int) с тем же результатом ошибки, который указывал, что я пытался переопределить signal:

TestDeclaration.c: 2: error: 'сигнал 'повторно объявлен как символ другого типа /usr/include/signal.h:93: ошибка: здесь было предыдущее объявление' сигнала '
TestDeclaration.c: 3: ошибка:' сигнал 'повторно объявлен как символ другого типа /usr / include / signal.h: 93: ошибка: предыдущее объявление 'signal' было здесь

Теперь я знаю, что оба испытания являются неправильными способами объявления, но почему они неверны?Почему оригинальный способ декларации НЕ РЕДАКЛАЦИЯ?

Ответы [ 5 ]

38 голосов
/ 14 сентября 2010

Это объявление функции, принимающей int и указатель на функцию (принимающей int, возвращающей void) и возвращающей указатель на функцию (принимающей int и возвращающей void).


Объяснение или руководство по интерпретации

Вы можете интерпретировать, рассматривая все в скобках как единый объект, а затем работая внутрь, используя правило "объявление следует за использованием".

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

Сущность в скобках выглядит как функция, принимающая intи возвращая void.

Удаление внешней части:

*signal(int sig, void (*func)(int))

Итак, signal принимает некоторые параметры и возвращает то, что может быть разыменовано (из-за ведущего *) для формирования функции, принимающей int и возвращающей void.

Это означает, что signal - это функция, возвращающая указатель на функцию (принимающая int и возвращающая void).

Глядя на параметры, требуется int (т. Е. sig)d void (*func)(int), который является указателем на функцию (принимает int и возвращает void).

8 голосов
/ 14 сентября 2010

Это один из классических примеров того, как запутанные объявления C могут стать.Чтобы понять это объявление, обычно полезно ввести typedef:

typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t func);

Typedef объявляет указатель на функцию (принимает параметр int и ничего не возвращает).Функция signal теперь может рассматриваться как функция, которая принимает два параметра (int и указатель на функцию) и возвращает указатель на функцию.

Это также можно получить из исходного объявления,но это требует немного практики.Обычный способ - начать с идентификатора, который называет самую внешнюю сущность (signal в данном случае):

signal - это ...

ТогдаВы читаете правильно, пока не найдете непревзойденную закрывающую скобку или конец объявления: void (*<em>signal</em><s>(int sig, void (*func)(int)</s>)(int)

signal - это функция, принимающая ... возврат ...

Теперь вы можете выбрать сначала синтаксический анализ параметров или сначала возвращаемое значение.Я сделаю возвращаемое значение первым.Для этого вы читаете в обратном направлении, чтобы найти соответствующие открытые скобки: void <s>(<em></s><em>signal( /</em> ... */ )</em><s>)</s>(int)

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

Читая взад и вперед, вы получаете последовательные этапы:

`сигнал - это функция, принимающая ... возвращающая указатель на (функция, принимающая ... возвращающаяся ...)

`сигнал - это функция, принимающая ... возвращающая указатель на (функция, принимающая ... возвращающая пустоту)

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

`signal - это функция, принимающая два параметра: (int) и (указатель на функцию, принимающую int и возвращающая void), и возвращающая указатель на (функция, принимающая int ивозвращение недействительным)

1 голос
/ 25 июля 2014

Давайте рассмотрим пример того, как можно использовать это противное объявление:

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

Без слишком многословия можно сказать, что "сигнал" - это функция с двумя параметрами, которая возвращает функцию.

#include <stdio.h>

// First function that could be returned by our principal function
// Note that we can point to it using void (*f)(int)
void car_is_red(int color)
{
    printf("[car_is_red] Color %d (red) is my favorite color too !\n", color);
}

// Second function that could be returned by our principal function
// Note that we can point to it using void (*f)(int)
void car_is_gray(int color)
{
    printf("[car_is_gray] I don't like the color %d (gray) either !\n", color);
}

// The function taken as second parameter by our principal function
// Note that we can point to it using void (*func)(int)
void show_car_type(int mod)
{
    printf("[show_car_type] Our car has the type: %d\n",mod);
}

/* Our principal function. Takes two parameters, returns a function. */
void (* show_car_attributes(int color, void (*func)(int)) )(int)
{
    printf("[show_car_attributes] Our car has the color: %d\n",color); // Use the first parameter

    int mod = 11;  // Some local variable of our function show_car_attributes()
    func(mod);  // Call the function pointed by the second parameter (i.e. show_car_type() )

    // Depending on color value, return the pointer to one of two functions
    // Note that we do NOT use braces with function names
    if (color == 1)
        return car_is_red;
    else
        return car_is_gray;
    }


//main() function
int main()
{
    int color = 2;   // Declare our color for the car
    void (*f)(int);  // Declare a pointer to a function with one parameter (int)

    f = show_car_attributes(color, show_car_type); // f will take the return 
           // value of our principal function. Stated without braces, the 
           // parameter  "show_car_types" is a function pointer of type 
           // void (*func)(int).

    f(color);  // Call function that was returned by show_car_attributes()

    return 0;
}

Посмотрим, что будет выводиться:

Если цвет = 1

[show_car_attributes] Our car has the color: 1
[show_car_type] Our car has the type: 11
[car_is_red] Color 1 (red) is my favorite color too !

Если цвет = 2

[show_car_attributes] Our car has the color: 2
[show_car_type] Our car has the type: 11
[car_is_gray] I don't like the color 2 (gray) either !
1 голос
/ 01 января 2014

Мнемоника, которую я создал много лет назад, которая неоценима при попытке понять сложные типы:

Remember these rules for C declares
And precedence never will be in doubt
Start with the Suffix, Proceed with the Prefix
And read both sets from the inside, out.

За исключением случаев, когда скобки меняют этот приоритет, конечно.

Применяя его к этомуcase:

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

signal is:
  [inside parentheses]
  [suffix ()] a function, whose arguments are
    sig, which is [prefix int] an integer, and
      func, which is:
         [inside parentheses]
           [no suffix within these parens]
           [prefix *] a pointer to
         [suffix ()] a function, whose argument is
           an int
         [no more suffixes]
         [prefix void] and which returns void
         [no more prefixes]
       [no more arguments]
     [prefix *] And which returns a pointer to
     [no more prefixes within these parens]
   [suffix ()] a function, whose argument is
      an int
   [no more suffixes]
   [prefix void] and which returns void.

Немного потренировавшись, вы доберетесь до точки, где вы можете сделать все это на лету:

"Signal is function, whose arguments are:
    sig, an integer,
    and func, a pointer to a function whose argument is an int and which returns void
... which returns a pointer to a function that takes int as an argument and returns void.

(извините за ошибку в первый разout - я не практикуюсь.)

Да, эта мнемоника (с подразумеваемым «кроме скобок, конечно») работает для всех объявлений C, независимо от того, насколько плохо работают указатели, массивы и функции

Это ДЕЙСТВИТЕЛЬНО полезный навык, который нужно иметь при попытке выяснить, как работает чужой код ... или даже выяснить что-то свое, чего вы давно не видели.

Но, да, лучший способ справиться с чем-то, что, как вы думаете, люди не смогут прочитать с первого взгляда, - это создать его в слоях с typedef. Типы компонентов, вероятно, будут полезны для них.и делает шаг за шагом, чтобы люди не терялись, пытаясь понять, какая скобка соответствует какой.Будьте добры к следующему человеку, который прикоснется к вашему коду!

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

Кстати, есть также инструменты "C Explainer", которые будут анализировать ошибки C и выполнять преобразование в английское описание для вас.Моя была названа CEX, по понятным причинам, но существует множество других, и вы сможете найти такой, если не хотите передавать этот навык мокрой посуде или если кто-то вручает вам что-то действительно уродливое, чтобы вы могли отследить.

0 голосов
/ 14 сентября 2010

Возвращает указатель на функцию, которая принимает:

  • целое число в качестве аргумента первого аргумента и
  • указатель на функцию (которая принимает int и возвращает void) как аргумент в качестве второго аргумента.

И принимает целочисленный аргумент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...