Понимание прототипов функций в C - PullRequest
1 голос
/ 15 августа 2011

1) Код ниже:

a)

void main()
{
float x;
fun(x,x,x);
}
fun(float x,float x){}

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

б) Теперь

void main()
{
float x;
fun(x,x,x);
}
fun(double x,double x){}

работает. Когда компилятор может проверить тип данных, почему он не может проверить количество аргументов? Которое согласно определению должно быть только два !!!?

2) Кроме того, каково значение использования не прототипов, таких как void fun ();какая разница, когда они не помогают проверять параметры, передаваемые при вызове функции, почему они поддерживаются ??

Спасибо:)

Ответы [ 6 ]

1 голос
/ 20 августа 2011

Я отвечу снова (и надеюсь, что вас не уценят).Так как вы, похоже, все еще в замешательстве.

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

Первое, что нужно знать, это то, что c и c ++ читают исходный файл сверху вниз, чтобы понять, что вы делаете;они не смотрят вперед, не возвращаются и не корректируют свою интерпретацию, если находят новую информацию (в отличие от, скажем, C #).

Так что в вашем коде, когда компилятор видит

fun(x,x,x);

Это спрашивает себя.«Я знаю, что такое« весело ».Ответ: нет.Компилятор c ++ в этот момент откажется

x.cpp: In function ‘int main()’:
x.cpp:4: error: ‘fun’ was not declared in this scope

Чтобы исправить это, вы должны объявить fun прежде, чем (имеется в виду ранее в исходном файле) вы попытаетесь использовать его.Вы делаете это, помещая

void fun(float, float);

в начале файла.Это говорит компилятору: «где-то будет функция« fun », которая принимает 2 float», компилятор знает, что делать то, что он потом видит, вызов fun.

Теперь, что с Си.чтобы быть полезным (или не зависящим от вашей точки зрения)

C пытается угадать, что вы имеете в виду (учитывая, что вы ничего не сказали)

fun(x,x,x);

по умолчанию предполагается, чтоfun - это функция, которая принимает 3 int и возвращает int.Он считает, что это может быть правильно, и если это не так, то вы исправите это. Примечание - он не смотрел, какие параметры вы пытались передать в удовольствие.Он просто решает, что все неизвестные функции принимают и возвращают целые числа.

Итак, теперь вы ознакомились с правилами продвижения и т. Д. Вы пытались вызвать функцию, которая принимает целочисленные значения, передавая значения типа float или double.Это будет работать

В своем вопросе вы говорите, что float не работает, но работает дважды.В моих тестах оба компилируются, но я получаю предупреждение компилятора.Однако у вашего определения fun нет возвращаемого типа (в данном случае void).Если я использую ваш точный синтаксис, то ни один из них не компилирует

Advice, всегда сначала объявляйте функции.Используйте C ++ как «лучший компилятор C».Это намного строже и полезнее.

0 голосов
/ 15 августа 2011

Когда компилятор может проверить тип данных, почему он не может проверить количество аргументов? Которое согласно определению должно быть только двумя !!!?

Это неопределенное поведение parсовершенство.Компилятор позволяет вам сделать это, потому что он предполагает, что вы знаете, что делаете.Если вы установите уровни предупреждений и не получите предупреждение об этом, я бы переключился на другой компилятор.Одно дело предполагать, что ты знаешь, что делаешь.Но другое дело предположить, что вы не делаете никаких ошибок, не предупреждая вас об этом.Как вы уже указали, это технически возможно сделать, и это не сложно.

В чем заключается важность разрешения не прототипов, таких как void fun ();какая разница, когда они не помогают в проверке параметров, передаваемых при вызове функции, почему они поддерживаются ??

Во-первых, они для обратной совместимости.У старого стандартного C не было прототипов.Во-вторых, полезно иметь указатель на функцию, который не содержит прототип:

void f(int i) { /* ... */ }
void g(double d) { /* ... */ }

int main(void) {
  typedef void ftype();
  ftype *ptr[] = { &f, &g };
  ptr[0](42);
  ptr[1](3.1415);
}

Этот код действителен, и его поведение хорошо определено.

0 голосов
/ 15 августа 2011

Другие люди объяснили, почему он сделал то, что сделал, но стоит упомянуть, что именно поэтому многие программы на С читаются снизу вверх.Следующие результаты будут иметь правильные и ожидаемые результаты (ну, тип возврата main - int, но это просто педантично):

fun(float x,float x){}

void main()
{
    float x;
    fun(x,x);
}

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

0 голосов
/ 15 августа 2011

Прототип должен появиться за до вызова функции, чтобы быть полезным; либо определите функцию до ее вызова:

void fun (float x, float y) {} // IMPLICIT TYPING IS BAD JUJU!

int main(void) // Unless the documentation for your compiler *explicitly* says
               // that "void main()" is a legal signature, main should
               // always return int.
{
  float x;
  fun(x,x,x);
  return 0;
}

или объявить это отдельно:

int main(void)
{
  void fun(float x, float y);
  float x;
  fun(x,x,x);
  return 0;
}

void fun(float x, float y) {}

Я предпочитаю первую версию.

0 голосов
/ 15 августа 2011

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

Она автоматически переводит float в double, char и short в int и принимает возврат int.

Второйпример работает, хотя количество аргументов неверно, потому что их слишком много, что обычное соглашение о вызовах молча исправляет.

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

0 голосов
/ 15 августа 2011

Оба должны потерпеть неудачу при компиляции; какой компилятор вы используете

Также - вы не используете прототипы функций; прототип функции - это спецификация вызываемой функции перед вызовом

...