Предупреждение компилятора - что-то не так в списках параметров

Я пытаюсь скомпилировать следующий исходный код, который успешно компилируется как на gcc, так и на Microsoft cl.exe.

void SomethingBeforeExit();

void SomethingBeforeExit()
{
    // some code
    _exit(0);
}

int main(int argc, char *argv[])
{
    // some code
    atexit(SomethingBeforeExit);
}

Однако я получаю предупреждение C4113 от cl.exe со следующим сообщение:

SomeCode.c(10): warning C4113: 'void (__cdecl *)()' differs in parameter lists from 'void (__cdecl *)(void)'

Как я уже сказал, исходный код по-прежнему успешно компилируется и работает. Моя цель — предотвратить появление этого предупреждения в cl, поскольку gcc не генерирует никаких предупреждений при компиляции.

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

Я использую VS14 и C/C++ 19.00.23918 for x86 для компиляторов cl.exe и gcc v5.4.0 для сравнения сгенерированных предупреждений.


person David Refoua    schedule 03.08.2017    source источник
comment
Правило довольно ясное: функция, которую вы передаете atexit, должна быть (void). По историческим причинам () не эквивалентно (void) в (некоторых версиях) C, как в C++.   -  person David Schwartz    schedule 03.08.2017
comment
Возможный дубликат Является лучше использовать аргументы C void void foo(void) или not void foo()?   -  person cadaniluk    schedule 03.08.2017
comment
@Downvoter не является прямым повторяющимся вопросом, я просто не знал, как обрабатывалось объявление (), как заявил @David Schwartz. Этот вопрос касается конкретно предупреждения C4113, которое связано с неправильной передачей параметров, а не о том, лучше ли использовать (void), чем ().   -  person David Refoua    schedule 03.08.2017
comment
Какая строка сгенерировала предупреждение: atexit(SomethingBeforeExit); или void SomethingBeforeExit() {?   -  person chux - Reinstate Monica    schedule 04.08.2017
comment
@chux первый.   -  person David Refoua    schedule 04.08.2017


Ответы (2)


В C пустые круглые скобки в объявлении функции не означают отсутствие параметров. Вместо этого это означает любое количество параметров (аналогично ... в C++). Возможно, вы хотели объявить void SomethingBeforeExit(void).

person Dan Waxman    schedule 03.08.2017
comment
Спасибо, это правильный ответ. Я не понял, что identifier2 в документации MS относится к переданным параметрам. - person David Refoua; 03.08.2017

OP не использует совместимый компилятор C99/C11. Код действителен C (C99/C11).

// declares function SomethingBeforeExit and no info about it parameters.
void SomethingBeforeExit();

// declares/defines function SomethingBeforeExit and 
// indirectly specifies the parameter list is `(void)`
// This does not contradict the prior declaration and so updates the parameter signature.
void SomethingBeforeExit() {
   ...  
}

int main(int argc, char *argv[]) {
  ...
  // `atexit() expects a `void (*f)(void))`
  atexit(SomethingBeforeExit);

В C89 определение void SomethingBeforeExit() { ...} по-прежнему ничего не говорит о списке параметров. Это, вероятно, причина проблемы OP.

Чинить:

void SomethingBeforeExit(void) { 
  ...
}

Предыдущее объявление void SomethingBeforeExit(); не нужно обновлять, но было бы неплохо также обновить его, чтобы проверка параметров могла происходить с кодом, который не видит определения.

person chux - Reinstate Monica    schedule 03.08.2017
comment
Определение void SomethingBeforeExit(){ … } по-прежнему не является прототипом функции. Компилятору не нужно жаловаться, если вы вызываете SomethingBeforeExit(1, 3.14159, "Apocalypse Then"):, поскольку в области видимости нет прототипа, который сделал бы его недействительным. - person Jonathan Leffler; 04.08.2017
comment
Как я прочитал C11 §6.7.6.3 14 Пустой список в объявлении функции, который является частью определения этой функции, указывает, что функция не имеет параметров. указывает, что функция не принимает параметров — она же (void). void SomethingBeforeExit( etc. (как объявление/определение), по крайней мере, прототипирует функцию как ничего не возвращающую. - person chux - Reinstate Monica; 04.08.2017
comment
@JonathanLeffler Ну, по крайней мере, моя версия gcc 5.4.0 с вами согласна. - person chux - Reinstate Monica; 04.08.2017
comment
100% согласились с тем, что тип возвращаемого значения объявлен — это, конечно, было частью совместимости с достандартным C. - person Jonathan Leffler; 04.08.2017
comment
@JonathanLeffler Я использовал #define info_type(X) _Generic((X), \ void (*)(void): "void (*)(void)", \ int: "int", \ default: "?" \ ), void SomethingBeforeExit() { } void SomethingBeforeExitV(void) { } и puts(info_type(SomethingBeforeExit)); puts(info_type(SomethingBeforeExitV));, и оба печатали "void (*)(void)". Таким образом, с/без void делают для одного и того же типа функции, но gcc жалуется только на SomethingBeforeExitV(1), а не на SomethingBeforeExit(1) - Хммм. - Интересная находка. - person chux - Reinstate Monica; 04.08.2017
comment
Давайте продолжим обсуждение в чате. - person Jonathan Leffler; 04.08.2017
comment
Чат содержит подробности и много обсуждений, но, похоже, §6.9.1 Определения функций, §7 действительно означает, что определение функции SomeType FunctionName() { … } не предоставляет прототип функции, даже если компилятор знает, что это функция, которая не принимает никаких аргументы. Причина, вероятно, «историческая» и/или «это облегчает анализ грамматики языка». Предупреждения могут быть сгенерированы компилятором при неправильном использовании; это будет проблема QoI (качество реализации). - person Jonathan Leffler; 04.08.2017
comment
Джонатан Леффлер и я пришли к соглашению по всем пунктам, за исключением того, что мы расходимся в том, что SomeType FunctionName() { … } предоставляет прототип или нет. IAC, мы согласны, что SomeType FunctionName(void) { … } лучше. - person chux - Reinstate Monica; 04.08.2017
comment
@JonathanLeffler Дальнейшие исследования приближают меня к тому, что ваш SomeType FunctionName() { … } не предоставляет прототип функции. Я бы сказал, что теперь SomeType FunctionName() { … } предоставляет только старый стиль или прототип без информации о входных параметрах для функции. И нужно вернуть компилятору, что это функция, которая не принимает аргументов. Я бы сказал, что в этом нет никаких входных параметров. Мое тестирование _Generic и присваивания указателя было ошибочным. Возможно, я опубликую Q / A в будущем. МАК, SomeType FunctionName(void) { … } остается лучше. - person chux - Reinstate Monica; 04.08.2017
comment
Спасибо за обновления. Я считаю, что мы (и всегда были) на 100% согласны с тем, что явное определение SomeType FunctionName(void) { … } предпочтительнее (и объявление SomeType FunctionName(void); тоже). - person Jonathan Leffler; 04.08.2017
comment
Только что посмотрел правила грамматики в §6.7.6 Деклараторы. Существует правило direct-declarator с двумя соответствующими параметрами: direct-declarator ( parameter-type-list ) и direct-declarator ( identifier-list-opt ). Это означает, что версия с пустыми скобками имеет пустой необязательный список идентификаторов. parameter-type-list не может быть пустым. Он поддерживает , ... в конце, и за ним должно следовать хотя бы одно объявление параметра — потребуется некоторое жонглирование правилами грамматики, чтобы сделать список типов параметров необязательным вместо списка идентификаторов. Это можно было бы сделать (но не сделал). - person Jonathan Leffler; 04.08.2017