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

Понимание разницы между f() и f (void) в C и С++ раз и навсегда

Возможный дубликат:
является f (void), устаревшим в современных C и С++

Хорошо, поэтому я слышал разные мнения по этому вопросу и просто хочу убедиться, что правильно понял.

Для С++

Объявления void f(); и void f(void); означают точно то же самое, функция f не принимает никаких параметров. То же для определений.

Для C

Объявление void f(void); означает, что f не принимает никаких параметров.

Объявление void f(); означает, что функция f может иметь или не иметь параметров, и если это так, мы не знаем, какие параметры есть или сколько из них существует. Обратите внимание, что это НЕ то же самое, что и многоточие, мы не можем использовать va_list.

Теперь вот что интересно.

Случай 1

Декларация:

void f();

Определение:

void f(int a, int b, float c)
{
   //...
}

Случай 2

Декларация:

void f();

Определение:

void f()
{
   //...
}

Вопрос:

Что происходит во время компиляции в случаях 1 и 2, когда мы вызываем f с правильными аргументами, неправильными аргументами и без аргументов вообще? Что происходит во время выполнения?

Дополнительный вопрос:

Если я объявляю f аргументами, но определяю их без них, будет ли это иметь значение? Должен ли я обращаться к аргументам из тела функции?

4b9b3361

Ответ 1

Больше терминологии (C, а не С++): прототип функции объявляет типы своих аргументов. В противном случае функция не имеет прототипа.

void f();                      // Declaration, but not a prototype
void f(void);                  // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype

Объявления, которые не являются прототипами, являются задержками с pre-ANSI C, начиная с дней K & R C. Единственная причина использования объявления старого стиля - поддерживать двоичную совместимость со старым кодом. Например, в Gtk 2 есть объявление функции без прототипа - оно есть случайно, но оно не может быть удалено без нарушения двоичных файлов. Стандартные комментарии C99:

6.11.6 Объявление функций

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

Рекомендация: Я предлагаю скомпилировать весь код C в GCC/Clang с помощью -Wstrict-prototypes и -Wmissing-prototypes, в дополнение к обычному -Wall -Wextra.

Что произойдет

void f(); // declaration
void f(int a, int b, float c) { } // ERROR

Объявление не согласуется с телом функции! Это на самом деле ошибка времени компиляции, и это потому, что вы не можете иметь аргумент float в функции без прототипа. Причина, по которой вы не можете использовать float в функции unprototyped, состоит в том, что, когда вы вызываете такую ​​функцию, все аргументы получают повышение с помощью определенных рекламных акций по умолчанию. Здесь фиксированный пример:

void f();

void g()
{
    char a;
    int b;
    float c;
    f(a, b, c);
}

В этой программе a повышается до int 1 а c - до double. Поэтому определение f() должно быть:

void f(int a, int b, double c)
{
    ...
}

См. C99 6.7.6, пункт 15,

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

Ответ 1

Что происходит во время компиляции в случаях 1 и 2, когда мы вызываем f с правильными аргументами, неправильными аргументами и без аргументов вообще? Что происходит во время выполнения?

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

"Undefined поведение" является spec-speak для "мы не делаем никаких гарантий относительно того, что произойдет". Возможно, ваша программа потерпит крах, возможно, она будет работать нормально, возможно, она пригласит ваших родственников на ужин.

Есть два способа получить диагностику во время компиляции. Если у вас есть сложный компилятор с возможностями кросс-модульного статического анализа, вы, вероятно, получите сообщение об ошибке. Вы также можете получать сообщения для не-прототипированных деклараций функций с помощью GCC, используя -Wstrict-prototypes - который я рекомендую включить во всех ваших проектах (кроме файлов, которые используют Gtk 2).

Ответ 2

Если я объявляю f аргументами, но определяю их без них, будет ли это иметь значение? Должен ли я обращаться к аргументам из тела функции?

Он не должен компилироваться.

Исключения

На самом деле есть два случая, когда аргументам функции разрешено не соглашаться с определением функции.

  • Допустим char * передать функцию, ожидающую void *, и наоборот.

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

Сноски

1: возможно, что char продвигается до unsigned int, но это очень необычно.

Ответ 2

Все дело в том, что если вы используете C99 или более позднюю версию (и если вы не застряли на старой встроенной системе или что-то в этом роде, вероятно, вам следует использовать что-то более современное).

Раздел C99/C11 6.11.6 Future language directions, Function declarators указывает:

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

Следовательно, вам следует избегать использования таких вещей, как void f();.

Если он принимает параметры, перечислите их, образуя правильный прототип. Если нет, нам void, чтобы окончательно указать, что он не принимает никаких параметров.

Ответ 3

В С++, f() и f (void) одинаковы

В C, они различны, и любое количество аргументов может быть передано при вызове функции f(), но никакой аргумент не может быть передан в f (void)

Ответ 4

В чистом C это приводит к ошибке: error C2084: function 'void __cdecl f(void )' already has a body

void f(void);
void f( );

int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(void) {

}

void f( ) {

}

.

 fvoid.c line(19) : error C2084: function 'void __cdecl f(void )' already has a body

Но в Pure С++ он будет компилироваться без ошибок.

Функции перегрузки (только С++, C не имеет перегрузки)

Вы перегружаете имя функции f, объявляя более одной функции с именем f в той же области. Объявления f должны отличаться друг от друга по типам и/или количеству аргументов в списке аргументов. Когда вы вызываете перегруженную функцию с именем f, правильная функция выбирается путем сравнения списка аргументов вызова функции с списком параметров каждой из перегруженных функций-кандидатов с именем f.

Пример:

#include <iostream>
using namespace std;

void f(int i);
void f(double  f);
void f(char* c);


int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(int i) {
  cout << " Here is int " << i << endl;
}
void f(double  f) {
  cout << " Here is float " << f << endl;
}

void f(char* c) {
  cout << " Here is char* " << c << endl;
}

выход:

 Here is int 10
 Here is float 10.1
 Here is char* ten