Подтвердить что ты не робот

Лучше ли использовать аргументы void void foo (void) "или нет" void foo() "?

Что лучше: void foo() или void foo(void)? С void это выглядит уродливым и непоследовательным, но мне сказали, что это хорошо. Это правда?

Изменить: я знаю, что некоторые старые компиляторы делают странные вещи, но если я использую только GCC, void foo() Хорошо? Будет ли принят foo(bar);?

4b9b3361

Ответ 1

void foo(void);

Это правильный способ сказать "нет параметров" в C, а также работает на С++.

Но:

void foo();

Означает разные вещи в C и С++! В C это означает, что "может принимать любое количество параметров неизвестных типов", а в С++ оно означает то же, что и foo(void).

Переменные функции списка аргументов по своей сути являются un-typesafe и их следует избегать, когда это возможно.

Ответ 2

Существует два способа указания параметров в C. Один использует список идентификаторов, а другой использует список типов параметров. Список идентификаторов может быть опущен, но список типов не может. Итак, чтобы сказать, что одна функция не принимает аргументов в определении функции, вы делаете это с помощью (опущенного) списка идентификаторов

void f() {
    /* do something ... */
}

И это с типом списка параметров:

void f(void) {
    /* do something ... */
}

Если в списке типов параметров только один тип параметра недействителен (он не должен иметь имени тогда), то это означает, что функция не принимает никаких аргументов. Но эти два способа определения функции имеют разницу в отношении того, что они объявляют.

Списки идентификаторов

Первое определяет, что функция принимает определенное количество аргументов, но ни счет не передается, ни типы того, что необходимо - как и для всех объявлений функций, которые используют списки идентификаторов. Таким образом, вызывающий должен знать типы и счет точно в руки. Поэтому, если вызывающий абонент вызывает функцию, дающую ему некоторый аргумент, поведение undefined. Например, стек может быть поврежден, потому что вызываемая функция ожидает другого макета, когда он получает контроль.

Использование списков идентификаторов в параметрах функций устарело. Он использовался в старые времена и по-прежнему присутствует в большом количестве кода производства. Они могут вызывать серьезную опасность из-за этих рекламных акций (если тип продвинутого аргумента не соответствует типу параметра определения функции, поведение равно undefined!) И, конечно же, гораздо менее безопасно. Поэтому всегда используйте функцию void для функций без параметров, как в только декларациях, так и в определениях функций.

Список типов параметров

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

Второй способ объявления функции имеет множество преимуществ. Разумеется, проверяется количество и типы параметров. Другое отличие состоит в том, что, поскольку компилятор знает типы параметров, он может применять неявные преобразования аргументов к типу параметров. Если список типов параметров отсутствует, это невозможно, и аргументы преобразуются в продвигаемые типы (которые называются продвижением аргументов по умолчанию). char станет int, например, в то время как float станет double.

Составной тип для функций

Кстати, если файл содержит как пропущенный список идентификаторов, так и список типов параметров, список типов параметров "выигрывает". Тип функции в конце содержит прототип:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

Это потому, что обе декларации не говорят ничего противоречивого. Во-вторых, однако, было что сказать дополнительно. Который является тем, что один аргумент принят. То же самое можно сделать в обратном

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

Первая определяет функцию, использующую список идентификаторов, тогда как вторая предоставляет для нее прототип, используя объявление, содержащее список типов параметров.

Ответ 3

void foo(void) лучше, потому что он явно говорит: никаких параметров не разрешено.

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

Ответ 4

C99 цитаты

Этот ответ призван процитировать и объяснить соответствующие части C99 N1256 стандартного проекта.

Определение декларатора

Термин declarator выйдет очень много, поэтому дайте понять.

Из языковой грамматики мы обнаружим, что следующие символы подчеркивания являются деклараторами:

int f(int x, int y);
    ^^^^^^^^^^^^^^^

int f(int x, int y) { return x + y; }
    ^^^^^^^^^^^^^^^

int f();
    ^^^

int f(x, y) int x; int y; { return x + y; }
    ^^^^^^^

Деклараторы являются частью деклараций и определений функций.

Существует 2 типа деклараторов:

  • список типов параметров
  • список идентификаторов

Список типов параметров

Заявления выглядят следующим образом:

int f(int x, int y);

Определения выглядят следующим образом:

int f(int x, int y) { return x + y; }

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

Список идентификаторов

Определения выглядят следующим образом:

int f(x, y)
    int x;
    int y;
{ return x + y; }

Заявления выглядят следующим образом:

int g();

Мы не можем объявить функцию с непустым списком идентификаторов:

int g(x, y);

потому что 6.7.5.3 "Объявление функций (включая прототипы)" говорит:

3 Список идентификаторов в объявлении функции, который не является частью определения этой функции, должен быть пустым.

Он называется списком идентификаторов, потому что мы указываем только идентификаторы x и y на f(x, y), типы появляются после.

Это более старый метод и больше не должен использоваться. 6.11.6 Объявление функций говорит:

1 Использование деклараторов функций с пустыми скобками (не деклараторы типов параметров прототипа) является устаревшей функцией.

а введение объясняет, что является устаревшей особенностью:

Некоторые функции устаревают, а это означает, что они могут рассматриваться как отмена в будущем пересмотра настоящего международного стандарта. Они сохраняются, потому что их широкого использования, но их использование в новых реализациях (для реализации функции) или новые программы (для языка [6.11] или функций библиотеки [7.26]) не рекомендуется

f() vs f (void) для объявлений

Когда вы пишете просто:

void f();

это обязательно объявление списка идентификаторов, потому что 6.7.5 "Declarators" говорит, что грамматика определяет как:

direct-declarator:
    [...]
    direct-declarator ( parameter-type-list )
    direct-declarator ( identifier-list_opt )

поэтому только версия списка идентификаторов может быть пустым, поскольку она является необязательной (_opt).

direct-declarator - единственная грамматика node, которая определяет скобку (...) части декларатора.

Итак, как мы можем устранить и использовать лучший список параметров без параметров? 6.7.5.3 Объявление функций (включая прототипы) говорит:

10 Частный случай неименованного параметра типа void как единственного элемента в списке указывает, что функция не имеет параметров.

Итак:

void f(void);

- путь.

Это малый синтаксис, явно разрешенный, поскольку мы не можем использовать аргумент типа void любым другим способом:

void f(void v);
void f(int i, void);
void f(void, int);

Что может случиться, если я использую объявление f()?

Возможно, код будет скомпенсирован просто: 6.7.5.3 Объявление функций (включая прототипы):

14 Пустой список в объявлении функции, который не является частью определение этой функции указывает, что никакой информации о количестве или типах параметры поставляются.

Итак, вы можете сойти с рук:

void f();
void f(int x) {}

В других случаях UB может подкрадываться (и если вам повезет, компилятор скажет вам), и вам будет сложно понять, почему:

void f();
void f(float x) {}

Смотрите: Почему пустое объявление работает для определений с аргументами int, но не для аргументов float?

f() и f (void) для определений

f() {}

против

f(void) {}

похожи, но не идентичны.

6.7.5.3 Объявление функций (включая прототипы) говорит:

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

который похож на описание f(void).

Но все же... кажется, что:

int f() { return 0; }
int main(void) { f(1); }

соответствует undefined, а:

int f(void) { return 0; }
int main(void) { f(1); }

не соответствует, как обсуждалось в: Почему gcc разрешает передавать аргументы функции, не имеющей аргументов?

TODO понять, почему именно. Имеет отношение к тому, чтобы быть прототипом или нет. Определите прототип.

Ответ 5

Помимо синтаксических различий, многие люди также предпочитают использовать void function(void) по практическим соображениям:

Если вы используете функцию поиска и хотите найти реализацию функции, вы можете выполнить поиск function(void), и она вернет прототип, а также реализацию.

Если вы опустили второй void, вам нужно искать function() и, следовательно, также найти все вызовы функций, что затрудняет поиск фактической реализации.

Ответ 6

В С++ разница нет в main() и main(void).

Но в C, main() будет вызываться с любым количеством параметров.

Пример:

main ( ){
    main(10,"abc",12.28);
    //Works fine !
    //It won't give the error. The code will compile successfully.
    //(May cause Segmentation fault when run)
}

main(void) будет вызываться без каких-либо параметров. Если мы попытаемся пройти, то это приведет к ошибке компилятора.

Пример:

main (void) {
     main(10,"abc",12.13);
     //This throws "error: too many arguments to function ‘main’ "
}