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

Почему С++ более ограничительный в отношении прямого объявления прототипов функций (подписи)?

Я заметил, что С++ более ограничительный, чем C, в отношении объявления сигнатур функций перед их использованием, даже если определения функций объявляются после функции, которая на самом деле их вызывает?

Я всегда думал, что C более ограничительный, но похоже, что это не так.

Почему философия изменилась при разработке стандартов для языка программирования на С++?

Например, следующий код выполняет компиляцию в команде gcc, но выводит ошибку при попытке скомпилировать с помощью g++

#include<stdio.h>

int main()
{
    int a=sum(4,6);
    printf("%d",a);
    return 0;
}

int sum(int a,int b)
{
    return a+b;
}

Ошибка

‘sum’ was not declared in this scope
4b9b3361

Ответ 1

В старом (до C99) стандарте C есть вещь, называемая "объявлением неявной функции", которая была удалена с C99. Поэтому, если вы компилируете в режиме C90, компилятор должен поддерживать эту "функцию". В то время как в С++ "объявление неявной функции" никогда не было. Так что ошибки GCC. Ваш код недействителен в современных C (C99 или более поздних).

Скомпилируйте более строгие ключи компилятора (например, -std=c99 -Wall -Wextra -pedantic-errors) и обратите внимание на все диагностические операции.

Ответ 2

Я всегда думал, что C более ограничительный, но похоже, что это не так.

У вас есть это назад. Почти во всех местах, где С++ не является надмножеством C, это связано с тем, что С++ является более ограничительным. Система типа С++ более строгая, чем система типа C, унаследованные функции, такие как те, которые вы отключили ( "неявное объявление" ), удалены, есть еще много зарезервированных слов и т.д.

Верно, что С++ имеет гораздо больше возможностей, чем C, но вы не должны путать количество функций, которые язык имеет с отсутствием ограничений. Важнейшим аспектом философии дизайна семейства языков ML/Haskell является предоставление множества и множество функций, но также много и много строгости.

Ответ 3

C первоначально разрешенные функции вызываются из программы, не будучи определенными, позволяя им быть "определены позже". И тогда, если вы не определили функцию, компилятор просто составил соглашение о вызове, такое как "ok, я не знаю, что возвращает эта функция, поэтому давайте предположим, что он возвращает int". Аналогичные предположения могут быть сделаны для параметров... которые вы могли бы опционально определить для типов. Старые функции "C & R style" C выглядели как

int func (a, b)
  int a;
  int b;
{
   ...
}

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

int func (int a, int b); // function prototype since the parameter types are explicit

Все поведение объявления неявной функции было простой опасной бессмыслицей и приводило к фатальным ошибкам. Тем не менее это опасное поведение было лишь частично прекращено в 1990-х годах стандартизации. Компилятору все же разрешалось делать неявные предположения о функции, если не было видно прототипа. (Например, именно поэтому malloc привык выходить из себя, если вы забыли включить stdlib.h.)

Вот почему ваш код компилируется, вы используете старую версию gcc (4.x или старше), которая по умолчанию имеет значение -std=gnu90, которая использует стандартные или нестандартные расширения стандарта 1990-х годов. Более новые версии gcc, 5.0 или новее по умолчанию равны -std=gnu11, которые являются текущими стандартными расширениями C (C11) +.

С++ никогда не допускал такого поведения, а C тоже исправлял его со стандартом C99 в 1999 году. Даже если у вас есть старый gcc-компилятор, вы должны скомпилировать его с помощью gcc -std=c99 -pedantic-errors, что означает "фактически следовать C, версия 1999 года". Затем вы получаете ошибки компилятора, если перед вызовом функции не отображается правильное объявление/определение функции.

Ответ 4

Есть много причин. Одна из них - перегрузка функции:

void func(double);
// void func(int);

int main()
{
    func(1);
}

Если я раскомментирую строку с помощью void func(int x);, она будет вызвана, иначе 1 будет увеличена до double и будет вызываться void func(double).

Ответ 5

Когда компилятор C видит вызов функции, о которой он не знает, он догадывается, каково должно быть возвращаемое значение и типы параметров. Тип возвращаемого значения рассматривается как int, и типы параметров считаются такими же, как и значение, принятое после применения "обычных рекламных акций".

Итак, если вы просто вызываете

double result = cube (1);

компилятор угадывает, что функция "куб" имеет один аргумент типа int и возвращает int.

Что произойдет, если эта "догадка" неверна? Tough. У вас есть поведение undefined, ваш код может упасть или ухудшиться.

Из-за этого "угадывания" сумма вызовов (4, 6) разрешена в C, а потому, что фактическая функция имела все правильные типы (два аргумента типа int, возвращаемое значение имеет тип), это фактически работает. Но это, безусловно, очень опасно.

Поскольку это так опасно, С++ не подразумевает деклараций (то есть компилятору С++ не разрешено угадывать типы аргументов, поэтому он не компилируется.

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