Namespaces
Variants

Function definitions

From cppreference.net

Определение функции связывает тело функции (последовательность объявлений и операторов) с именем функции и списком параметров. В отличие от объявления функции , определения функций разрешены только на уровне файла (вложенные функции не допускаются).

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

attr-spec-seq (необязательно) specifiers-and-qualifiers parameter-list-declarator function-body (1)
specifiers-and-qualifiers identifier-list-declarator declaration-list function-body (2) (до C23)

где

attr-spec-seq - (C23) необязательный список атрибутов , применяемых к функции
specifiers-and-qualifiers - комбинация
parameter-list-declarator - декларатор для функционального типа, который использует список параметров для обозначения параметров функции
identifier-list-declarator - декларатор для функционального типа, который использует список идентификаторов для обозначения параметров функции
declaration-list - последовательность объявлений, которые объявляют каждый идентификатор в identifier-list-declarator . Эти объявления не могут использовать инициализаторы, и единственный разрешенный спецификатор класса хранения register .
function-body - составной оператор , то есть последовательность объявлений и операторов в фигурных скобках, которая выполняется при каждом вызове этой функции
1) Определение функции в новом стиле (C89). Это определение одновременно вводит саму функцию и служит прототипом функции для любых будущих выражений вызова функции , принудительно выполняя преобразования из выражений аргументов в объявленные типы параметров.
int max(int a, int b)
{
    return a>b?a:b;
}
double g(void)
{
    return 0.1;
}
2) (до C23) Определение функции в старом стиле (K&R). Это определение не ведёт себя как прототип, и любые последующие выражения вызова функции будут выполнять стандартные преобразования аргументов.
int max(a, b)
int a, b;
{
    return a>b?a:b;
}
double g()
{
    return 0.1;
}

Содержание

Объяснение

Как и в случае с объявлениями функций , возвращаемый тип функции, определяемый спецификатором типа в specifiers-and-qualifiers и возможно модифицированный declarator как обычно в объявлениях , должен быть полным не-массивным типом объекта или типом void . Если возвращаемый тип должен быть cvr-квалифицированным, он корректируется до своей неквалифицированной версии для целей построения типа функции.

void f(char *s) { puts(s); } // тип возвращаемого значения - void
int sum(int a, int b) { return a+b; } // тип возвращаемого значения - int
int (*foo(const void *p))[3] { // тип возвращаемого значения - указатель на массив из 3 int
    return malloc(sizeof(int[3]));
}

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

В отличие от объявлений функций , безымянные формальные параметры не допускаются (иначе возникли бы конфликты в старых (K&R) определениях функций), они должны быть именованными, даже если не используются внутри функции. Единственное исключение — специальный список параметров ( void ) .

(до C23)

Формальные параметры могут быть безымянными в определениях функций, поскольку старые (K&R) определения функций были удалены. Безымянные параметры недоступны по имени внутри тела функции.

(начиная с C23)
int f(int, int); // объявление
// int f(int, int) { return 7; } // Ошибка до C23, корректно с C23
int f(int a, int b) { return 7; } // определение
int g(void) { return 8; } // OK: void не объявляет параметр

В теле функции каждый именованный параметр является lvalue выражением, они имеют автоматическую storage duration и block scope . Расположение параметров в памяти (или хранятся ли они в памяти вообще) не специфицировано: это часть calling convention .

int main(int ac, char **av)
{
    ac = 2; // параметры являются lvalues
    av = (char *[]){"abc", "def", NULL};
    f(ac, av);
}

См. оператор вызова функции для получения дополнительной информации о механике вызова функции и return для возврата из функций.

__func__

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

static const char __func__[] = "function name";

Этот специальный идентификатор иногда используется в комбинации с предопределёнными макроконстантами __FILE__ и __LINE__ , например, в assert .

(начиная с C99)

Примечания

Список аргументов должен быть явно указан в деклараторе, он не может быть унаследован из typedef

typedef int p(int q, int r); // p - это тип функции int(int, int)
p f { return q + r; } // Ошибка

В C89, specifiers-and-qualifiers был необязательным, и если опущен, возвращаемый тип функции по умолчанию был int (возможно, изменённым declarator ).

Кроме того, в старом стиле определения не требовалось объявления для каждого параметра в declaration-list . Любой параметр, объявление которого отсутствовало, имел тип int

max(a, b) // a and b have type int, return type is int
{
    return a>b?a:b;
}
(до C99)

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C.

DR Applied to Behavior as published Correct behavior
DR 423 C89 the return type might be qualified the return type is implicitly disqualified

Ссылки

  • Стандарт C17 (ISO/IEC 9899:2018):
  • 6.9.1 Определения функций (стр: 113-115)
  • Стандарт C11 (ISO/IEC 9899:2011):
  • 6.9.1 Определения функций (стр. 156-158)
  • Стандарт C99 (ISO/IEC 9899:1999):
  • 6.9.1 Определения функций (стр. 141-143)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
  • 3.7.1 Определения функций

Смотрите также

C++ документация для Определение функции