Продвижение аргументов по умолчанию в вызовах функций C

настройка

у меня есть несколько вопросов о промо-акциях аргументов по умолчанию при вызове функции в C. Вот раздел 6.5.2.2 "вызовы функций" пункты 6, 7 и 8 из стандарт C99 (pdf) (курсив и разбиты на списки для удобства чтения):

6

  1. если выражение, обозначающее вызываемую функцию, имеет тип не включает в себя прототип, целочисленные акции выполняются для каждого аргумента и аргументов, имеющих тип float звание double. Они называются по умолчанию аргумент акции.
  2. если количество аргументов не равно количеству параметров, поведение неопределено.
  3. если функция определена с типом, который включает в себя прототип, и либо прототип заканчивается многоточием (, ...) или типы аргументов после продвижения не совместимы с типами параметров, поведение не определено.
  4. если функция определена с типом, который не включает прототип, и типы аргументов после продвижения не совместимы с параметрами после продвижения, поведение не определено, за исключением следующих случаев:
    • один повышенный тип является целочисленным типом со знаком, другой повышенный тип-это соответствующий целочисленный тип без знака, и значение может быть представлено в обоих типах;
    • оба типа являются указателями на квалифицированные или неквалифицированные версии символьного типа или void.

пункт 7

  1. если выражение, обозначающее вызываемую функцию, имеет тип включает прототип аргументы неявно преобразуется, как бы по назначению, в типы соответствующих параметров, принимая тип каждого параметра за неквалифицированную версию его объявленного типа.
  2. многоточие в деклараторе прототипа функции приводит к остановке преобразования типа аргумента после последнего объявленного параметра. промо-акции аргументов по умолчанию выполняются для конечных аргументов.

пункт 8

  1. никакие другие преобразования не выполняются неявно; в частности, количество и типы аргументов не сравниваются с параметрами в определении функции, которые не включает Декларатор прототипа функции.

я знаю

  • на по умолчанию аргумент акции are char и short to int/unsigned int и float to double
  • необязательный аргументы для функций с переменным числом аргументов (как printf) подлежат промо-акции по умолчанию

для записи, мое понимание прототип функции это:

void func(int a, char b, float c);  // Function prototype
void func(int a, char b, float c) { /* ... */ }  // Function definition

вопрос

я очень тяжело groking все это. Вот несколько вопросов, которые у меня есть:

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

3 ответов


голосуют те, AProgrammer-это реальный товар.

для тех из вас, кто интересно почему дело обстоит так: в темные века до 1988 года не было такой вещи, как прототип функции в классическом " K&R "C, и продвижение аргументов по умолчанию было введено, потому что (a) были по существу" свободными", так как это стоит не больше, чтобы поместить байт в регистр, чем поместить слово в регистр, и (b) сократить потенциальные ошибки при передаче параметров. Эта вторая причина никогда не прерывала его, поэтому введение прототипов функций в ANSI C было самым важным изменением когда-либо в языке C.

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


  • (non variadic) параметры в функции с прототипом преобразуются в соответствующий тип, который может быть char, short, float.

  • параметры к функциям без прототипа и variadic параметры подлежат промотирования аргумента по умолчанию.

если вы определяете функцию с прототипом и используете ее без прототипа или наоборот, и у нее есть параметры типа char, short или float, у вас, вероятно, будет проблема во время выполнения. У вас будут такие же проблемы с variadic функциями, если продвигаемый тип не соответствует тому, что используется при чтении списка аргументов.

Пример 1: проблема при определении функции с прототипом и использовании ее без.

определение.c

void f(char c)
{
   printf("%c", c);
}

использовать.c

void f();

int main()
{
   f('x');
}

может произойти сбой, потому что будет передан int и функция ожидает char.

Пример 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) { /* ... */ }

это функция определение что включает в себя прототип.

" Prototyped "и" non-prototyped " - это просто атрибуты функции тип, и оба заявления и определения вводят тип функции.

таким образом, вы можете иметь объявление без прототипа:

void func();

или вы можете иметь определение без прототипа (стиль K&R C):

void func(a, b, c)
    int a;
    char b;
    float c;
{ /* ... */ }