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

Почему это законно в C?

Я пишу компилятор игрушек C для курса компилятора/языка в своем университете.

Я пытаюсь сформулировать семантику для разрешения символа в C и придумал этот тестовый пример, который я пробовал против обычных компиляторов clang и gcc.

void foo() { }
int main() { foo(5); } // foo has extraneous arguments

Большинство компиляторов только предупреждают о посторонних аргументах.

Вопрос: Какова основная причина этого?

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

Спасибо.

4b9b3361

Ответ 1

Функция с отсутствующими аргументами в прототипе считается неопределенным числом, а не нулем.

Если вам действительно нужны нулевые аргументы, это должно быть:

void foo (void);

Вариант с пустым списком - это отсрочка от древнего C, даже до того, как ANSI обрушилась на него, где у вас были такие вещи, как:

add_one(val)
int val;
{
    return val + 1;
}

int является типом возвращаемого по умолчанию и параметрами, указанным вне декларатора).

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

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

Ответ 2

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

int foo();
long bar();

и т.д. Это сказало компилятору, что имя ссылается на функцию, но ничего не указывает о количестве или типах параметров. Вероятно, самым большим изменением в оригинальном (1989) стандарте C было добавление "прототипов функций", что позволило объявить число и тип параметров, чтобы компилятор мог проверить, что вы передали при вызове функции. Чтобы поддерживать совместимость с существующим кодом, они решили, что пустой список параметров сохранит существующее значение, и если вы хотите объявить функцию, которая не принимает никаких параметров, вам нужно добавить вместо void список параметров: int f(void);.

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

1 Хотя вы все еще можете использовать многоточие для функции, которая принимает список переменных параметров, но когда/если вы это сделаете, вы можете передавать только типы POD в качестве параметров.

Ответ 3

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

Если вы написали:

void foo(void) {}

то вы бы предоставили прототип функции, которая не принимает никаких параметров.

gcc -Wstrict-prototypes поймает это. Для получения ошибки используйте -Werror=strict-prototypes. В стандарте никогда не указано, должно ли что-то быть предупреждением или ошибкой.

Ответ 4

Почему это законно в C?

Сначала просто для уточнения стандарта C не используется слово legal.

В терминологии C эта программа не строго соответствует:

void foo() { }
int main() { foo(5); } // foo has extraneous arguments

При компиляции этой программы диагностика не требуется из-за вызова функции foo(5): нарушение ограничений отсутствует. Но вызов функции foo с аргументом вызывает поведение undefined. Как любая программа, вызывающая поведение undefined, она не является строго соответствующей и компилятор имеет право отказаться от перевода программы.

В стандарте C объявление функции с пустым списком параметров означает, что функция имеет неопределенное количество параметров. Но определение функции с пустым списком параметров означает, что функция не имеет параметра.

Вот соответствующий параграф в стандарте C (все внимание мое):

(C99, 6.7.5.3p14) "Список идентификаторов объявляет только идентификаторы параметров функции. Пустой список в объявлении функции, который является частью определения этой функции , указывает, что функция имеет нет параметров."

Параграф стандарта C, в котором говорится, что вызов foo(5) - это undefined, является следующим:

(C99, 6.5.2.2p6) " Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает прототип, целые рекламные акции выполняются для каждого аргумента, а аргументы, которые имеют тип float повышается до double. Они называются аргументом по умолчанию Акции. Если количество аргументов не равно числу параметров, поведение undefined.

И из (C99, 6.9.1p7) мы знаем, что определение foo не обеспечивает прототип.

(C99, 6.9.1p7) "Если декларатор содержит список типов параметров, в списке также указаны типы всех параметров: такой декларатор также служит прототипом функции для последующих вызовов одной и той же функции в том же Если декларатор включает список идентификаторов, типы параметров должны быть объявлены в следующем списке объявлений".

См. ответ Комитета на отчет о дефекте № 317 за достоверный ответ на этот вопрос:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_317.htm