Продвижение аргументов по умолчанию в вызовах функций C - PullRequest
29 голосов
/ 10 августа 2009

Настройка

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

Пункт 6

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

Пункт 7

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

Пункт 8

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

Что я знаю

  • По умолчанию повышены аргументы : 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

Вопрос

Мне действительно тяжело все это делать. Вот несколько вопросов, которые у меня есть:

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

Ответы [ 3 ]

33 голосов
/ 10 августа 2009

Ответ Uprooted AProgrammer - это реальные товары.

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

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

33 голосов
/ 10 августа 2009
  • (Non-variadic) параметры для функций с прототипом преобразуются в соответствующий тип, который может быть char, short, float.

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

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

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

definition.c

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

use.c

void f();

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

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

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

definition.c

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

(Это определение очень старомодно)

use.c

void f(char c);

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

может потерпеть неудачу, потому что ожидается int, но будет передан символ.

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

15 голосов
/ 11 августа 2009

Ваша путаница проистекает из очень небольшого недопонимания терминологии - как декларации, так и определения могут включать прототипы (или нет):

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;
{ /* ... */ }
...