Может ли объявление функции C иметь более одного идентификатора? - PullRequest
1 голос
/ 24 февраля 2020

Есть C объявление функции signal от c -faq сайт пример 3:

void (*signal(int, void (*fp)(int)))(int);

Интересно, как может быть два идентификатора signal, fp и он все еще проходит компиляцию (я случайно добавил эту строку в свой существующий код, и она успешно компилируется)?

Ответы [ 2 ]

3 голосов
/ 24 февраля 2020

Имя fp обычно используется для переменных типа FILE *, но его можно использовать для любых других целей (с плавающей точкой, защита от промысла, указатель функции,…).

Объявление функции в вопросе эквивалентно объявлению signal() в стандарте C, за исключением того, что оно использует fp, когда стандарт использует func, а стандарт дает имя sig к первому аргументу signal().

Вы можете удалить fp из объявления функции (прототип); ничего существенного не меняется:

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

Вы также можете использовать значимые имена для аргументов повсюду:

void (*signal(int signum, void (*handler)(int signum)))(int signum);

Каждое вхождение signum указывает, где указан номер сигнала. Первый аргумент signal() сам по себе является номером сигнала; аргумент handler указывает, что функция-обработчик будет вызываться с номером сигнала; и самый внешний signum указывает, что указатель функции, возвращаемый signal(), относится к тому же типу, что и handler, и ему следует (is) передавать номер сигнала при вызове.

См. также Понимание typedefs для указателей функций в C.

1 голос
/ 24 февраля 2020

Нет, объявление / прототип функции C не может иметь более одного идентификатора. См. Справочную страницу и пример кода для signal на cplusplus.com здесь: http://www.cplusplus.com/reference/csignal/signal/.

В этом объявлении:

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

мы объявляем функция с именем signal, которая возвращает указатель на функцию, которая возвращает void и принимает int - ie: указатель на такую ​​функцию:

void func(int);

Входными данными для функции signal являются 1-й, int и 2-й, указатель на функцию, которая возвращает void и принимает int - опять же, a указатель на функцию, подобную func выше. Теперь, в C, при объявлении функции, которая возвращает указатель на функцию и принимает указатель на функцию, она выглядит как goobly-ga-gook (искаженный и сбивающий с толку), поэтому у всех нас возникает путаница первоначально (и снова и снова часто), глядя на объявления функций, такие как signal. Пара гораздо более понятных способов определения функции, такой как signal в C, чтобы сделать все это кристально чистым, похожи на это, вместо этого оба они идентичны похожим на goobly-gook:

// 1. typedef a function--call it `func_t` for "func type"
typedef void func_t(int);

// 2. Use the typedef above to define `signal`
func_t* signal(int sig, func_t* fp); // Ah, now this makes sense!

ИЛИ

// 1. typedef a *pointer to* a function--call it `func_p` for "pointer to a func type"
typedef void (*func_p)(int);

// 2. Use the typedef above to define `signal`
func_p signal(int sig, func_p fp); // Ah, now this makes sense too!

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

Пример:

my_module.h:

// Any of these 3 prototypes are equivalent, valid, and identical
// (although using sensible names which match between the header &
// source files is most clear and helpful to the reader!):
func_p signal(int sig, func_p fp);
func_p signal(int signal, func_p whatsupdude);
func_p signal(int, func_p);

my_module. c:

func_p signal(int sig, func_p fp)
{
    // define the function here
}

Теперь, как я сначала все это выяснил?
Ответ: Я посмотрел пример кода из cplusplus.com . Это помогло TON прояснить все это, поскольку вы можете видеть, как возвращаемое значение из signal присваивается prev_handler, который является указателем на функцию, и вы можете видеть, как my_handler, который определяется как функция, переданная в качестве второго параметра в signal! Для пиков, я, вероятно, должен также упомянуть, что эти 2 строки идентичны, и обе они совершенно корректны:

prev_handler = signal (SIGINT, my_handler);
prev_handler = signal (SIGINT, &my_handler);

Это потому, что если вы передаете функцию в качестве параметра, то компилятор знает, что он просто берет свой адрес в любом случае.

Пример кода Cplusplus:

/* signal example */
#include <stdio.h>      /* printf */
#include <signal.h>     /* signal, raise, sig_atomic_t */

sig_atomic_t signaled = 0;

void my_handler (int param)
{
  signaled = 1;
}

int main ()
{
  void (*prev_handler)(int);

  prev_handler = signal (SIGINT, my_handler);

  /* ... */
  raise(SIGINT);
  /* ... */

  printf ("signaled is %d.\n",signaled);


  return 0;
}

Ссылки:

  1. Напоминание о том, как вводить определения функций и указатели функций, поскольку это сбивает с толку почти всех и требует время от времени повторной проверки: http://www.iso-9899.info/wiki/Typedef_Function_Type
...