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

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

16 голосов
/ 15 марта 2011

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

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

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

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

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

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

Спасибо.

Ответы [ 4 ]

32 голосов
/ 15 марта 2011

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

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

void foo (void);

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

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

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

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

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

17 голосов
/ 15 марта 2011

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

int foo();
long bar();

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

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

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

5 голосов
/ 15 марта 2011

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

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

void foo(void) {}

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

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

4 голосов
/ 25 февраля 2012

Почему это допустимо в C?

Прежде всего, для пояснения, в стандарте C не используется слово legal .

.С точки зрения С, эта программа не строго соответствует :

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

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

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

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

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

Абзац стандарта C, которыйговорит, что вызов foo(5) - неопределенное поведение:

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

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

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

См. Ответ Комитета на Отчет о дефекте № 317 для получения достоверного ответа по этому вопросу:

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

...