Почему прототип функции не нужен в C, если нет возврата в функции, определенной после main ()? - PullRequest
0 голосов
/ 06 января 2020

Как и следовало ожидать, следующий код генерирует ошибку, если прототип double cubenum(); не объявлен как требуется в C.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

double cubenum(double number){
    double result = number * number * number;
    return result;
}

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

void cubenum(double number){
    double result = number * number * number;
    printf("Answer is: %f", result);
}

И когда объявлен прототип как пустота кубенум (); с приведенным выше определением кубена без возврата он генерирует следующую ошибку:

||=== Build: Debug in xxx(compiler: GNU GCC Compiler) ===|
C:\xxx\main.c||In function 'main':|
C:\xxx\main.c|10|error: invalid use of void expression|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Строка 10 была при тестировании: printf("Answer is: %f", cubenum(3.0));

Итак, вопрос:

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

G CC информация о версии

gcc (MinGW.org GCC-6.3.0-1) 6.3.0

Ответы [ 2 ]

3 голосов
/ 06 января 2020

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

Совершенно правильный современный стиль для вашей первой программы выглядел бы так:

#include <stdio.h>

double cubenum(double);

int main(void)
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

double cubenum(double number)
{
    double result = number * number * number;
    return result;
}

с прототипом предварительное объявление cubenum, double cubenum(double);

Это Важно понимать, что double cubenum(); НЕ является объявлением-прототипом в C, а скорее объявлением, которое говорит, что cubenum принимает любое число и тип аргументов. Если вы хотите указать, что cubenum принимает без аргументов, вам нужно будет написать double cubenum(void); По этой же причине я изменил int main() на int main(void).

Когда вы выходите предварительное объявление полностью,

#include <stdio.h>

int main(void)
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

double cubenum(double number)
{
    double result = number * number * number;
    return result;
}

C компилятор видит вызов cubenum без какого-либо предыдущего объявления вообще. Это было распространено в тех очень старых C программах, о которых я говорил. Они полагались на функцию под названием неявное объявление , которая была частью исходного стандарта C, но удалена из его редакции 1999 года (обычно известной как «C99»). По сути, компилятор предполагает, что программист намеревался написать int cubenum(); выше main, но лениво не учел это. Это означает, что cubenum принимает любое число и тип аргументов (НЕ то, что он не принимает аргументов) и возвращает int. Итак, оставив пока main, это похоже на то, что вы написали

int cubenum();
double cubenum(double number) { ... }

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

Теперь, когда вы изменяете cubenum, чтобы ничего не возвращать, значит ваша полная программа

#include <stdio.h>

int main(void)
{
    printf("Answer is: %f", cubenum(3.0));
    return 0;
}

void cubenum(double number)
{
    double result = number * number * number;
    printf("Answer is: %f", result);
}

, неявное объявление функции по-прежнему int cubenum() и прототип из определения функции void cubenum(double). В качестве еще одной функции совместимости для этих очень старых C программ они считаются , а не конфликтующими типами возврата, и компилятор принимает программу. Это связано с тем, что тип void был изобретен в стандарте 1989 C. Программы, написанные до этого, вместо этого давали бы cubenum вообще никакого типа возврата ...

cubenum(number)
    double number;
{
    double result = number * number * number;
    printf("Answer is: %f", result);
}

... что технически объявляет, что он возвращает int! Сразу после C89 эти программы были обновлены, чтобы дать своим функциям без возвращаемого значения тип void, но было слишком много работы, чтобы перестать полагаться на неявные объявления для них одновременно, поэтому компиляторы выросли в особый случай, когда int foo() и void foo() считаются не конфликтующими.

(Между прочим, из-за еще одного соображения обратной совместимости - «определения функций старого стиля», которые вы можете видеть в приведенном выше фрагменте кода - предпочтительнее) стиль в C состоит в том, чтобы поместить открывающую фигурную скобку определения функции на отдельной строке, даже если все остальные открывающие скобки «обнимаются».)

И, наконец, когда вы делаете поставить void cubenum(); выше main, только тогда компилятор официально узнает, что cubenum ничего не возвращает. Когда он это знает, он знает, что printf("%f", cubenum(3.0)); неверно, потому что он использует несуществующее возвращаемое значение cubenum, и отклоняет программу по причине .


Вы не должны полагаться ни на одну из этих функций обратной совместимости в новой программе. Я вижу, что вы используете G CC, поэтому настройте параметры компиляции примерно так:

-std=gnu11 -g -Og -Wall -Wpedantic -Wstrict-prototypes -Wold-style-definition -Werror

, что отключит почти все функции обратной совместимости. (Существует намного больше опций предупреждения , и вы можете рассмотреть возможность их включения. -Wwrite-strings и -Wextra особенно полезны для нового кода IMNSHO. ) (НЕ ИСПОЛЬЗУЙТЕ гиперконформный режим, -std=c11, пока вы не узнаете значительно больше о том, что вы делаете, он может сломать системные заголовки и разрешить ошибочное «триграфное» ошибочное впечатление, которое вам почти наверняка не нужно. )

1 голос
/ 06 января 2020

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

Потому что, когда у вас нет явного объявление для функции, некоторые C компиляторы будут делать это неявно. Обычно он дает int как для возвращаемого типа, так и для аргументов.

Достаточно хороший компилятор должен сообщить вам о неявном объявлении.

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

И когда прототип объявлен как void cubenum (); с приведенным выше определением кубена без возврата он генерирует следующую ошибку:

Это еще одна проблема, поскольку вы пытаетесь printf вернуть значение функции void.

...