Продвижение аргументов по умолчанию в вызовах функций C
-
12-09-2019 - |
Вопрос
Настраивать
У меня есть несколько вопросов о расширении аргументов по умолчанию при вызове функции в C.Вот раздел 6.5.2.2 «Вызовы функций» Пункты 6, 7 и 8 из Стандарт C99 (pdf) (выделено и разбито на списки для удобства чтения):
Параграф 6
- Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает прототип, целочисленное повышение выполняется для каждого аргумента, а аргументы типа
float
повышены доdouble
.Они называются продвижение аргументов по умолчанию.- Если количество аргументов не равно количеству параметров, поведение не определено.
- Если функция определена с типом, который включает в себя прототип, и либо прототип заканчивается многоточием (
, ...
) или типы аргументов после продвижения несовместимы с типами параметров, поведение не определено.- Если функция определена с типом, который не включает прототип, а типы аргументов после продвижения несовместимы с типами параметров после продвижения, поведение не определено, за исключением следующих случаев:
- один расширенный тип представляет собой целочисленный тип со знаком, другой расширенный тип представляет собой соответствующий целочисленный тип без знака, и значение может быть представлено в обоих типах;
- оба типа являются указателями на квалифицированные или неполные версии символьного типа или
void
.
Параграф 7
- Если выражение, обозначающее вызываемую функцию, имеет тип, который включает прототип, аргументы неявно преобразуются, как бы путем присваивания, в типы соответствующих параметров, при этом тип каждого параметра принимается как неполная версия его объявленного типа.
- Обозначение многоточия в деклараторе прототипа функции приводит к остановке преобразования типа аргумента после последнего объявленного параметра. Повышение аргументов по умолчанию выполняется для конечных аргументов.
Параграф 8
- Никакие другие преобразования не выполняются неявно;в частности, количество и типы аргументов не сравниваются с параметрами в определении функции, которые не включает декларатор прототипа функции.
Что я знаю
- А продвижение аргументов по умолчанию являются
char
иshort
кint
/unsigned int
иfloat
кdouble
- Необязательные аргументы функций с переменным числом аргументов (например,
printf
) подлежат повышению аргументов по умолчанию
Для справки, мое понимание прототип функции это:
void func(int a, char b, float c); // Function prototype
void func(int a, char b, float c) { /* ... */ } // Function definition
Вопрос
Мне очень трудно все это понять.Вот несколько вопросов, которые у меня есть:
- Действительно ли поведение прототипированных и непрототипированных функций так сильно различается, например, в отношении повышения по умолчанию и неявных преобразований?
- Когда происходит повышение аргументов по умолчанию?Всегда ли это?Или это только в особых случаях (например, с вариационными функциями)?Зависит ли это от того, прототипирована ли функция?
Решение
Проголосовал за ответ AProgrammer — это настоящие товары.
Для тех из вас, кому интересно почему дела обстоят так:в темные века до 1988 года не существовало такой вещи, как прототип функции в классическом «K&R» C, и были введены повышения аргументов по умолчанию, потому что (а) они были по существу «бесплатными», поскольку размещение байта не требовало больше затрат в регистре, чем помещать слово в регистр, и (б) сократить количество потенциальных ошибок при передаче параметров.Вторая причина так и не сработала, поэтому введение прототипов функций в ANSI C стало самым важным изменением, когда-либо существовавшим в языке C.
Что касается того, когда вступают в силу акции по умолчанию: Повышение аргумента по умолчанию используется именно тогда, когда ожидаемый тип аргумента равен неизвестный, то есть, когда нет прототипа или когда аргумент является вариационным.
Другие советы
(Невариативные) параметры функций с прототипом преобразуются в соответствующий тип, который может быть char, short, float.
Параметры функций без прототипов и переменных параметров подлежат повышению аргументов по умолчанию.
Если вы определяете функцию с прототипом и используете ее без прототипа или наоборот, и она имеет параметры типа char, short или float, у вас, вероятно, возникнут проблемы во время выполнения.У вас возникнут такие же проблемы с функциями с переменным числом аргументов, если расширенный тип не соответствует тому, который используется при чтении списка аргументов.
Пример 1:проблема при определении функции с прототипом и использовании без нее.
определение.c
void f(char c)
{
printf("%c", c);
}
использовать.c
void f();
int main()
{
f('x');
}
может потерпеть неудачу, потому что будет передано целое число, а функция ожидает символ.
Пример 2:проблема при определении функции без прототипа и использовании ее с ним.
определение.c
void f(c)
char c;
{
printf("%c", c);
}
(Это своего рода определение очень старомодное)
использовать.c
void f(char c);
int main()
{
f('x');
}
может потерпеть неудачу, поскольку ожидается int, но будет передан символ.
Примечание:вы заметите, что все функции стандартной библиотеки имеют типы, возникающие в результате повышения по умолчанию.Таким образом, они не вызвали проблем во время перехода, когда были добавлены прототипы.
Ваша путаница связана с очень небольшим непониманием терминологии - как объявления, так и определения могут включать прототипы (или нет):
void func(int a, char b, float c);
Это функция декларация включая прототип.
void func(int a, char b, float c) { /* ... */ }
Это функция определение включая прототип.
«Прототипизированный» и «непрототипированный» — это всего лишь атрибуты функции. тип, и как объявления, так и определения представляют тип функции.
Итак, вы можете иметь объявление без прототипа:
void func();
или вы можете иметь определение без прототипа (стиль K&R C):
void func(a, b, c)
int a;
char b;
float c;
{ /* ... */ }