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;
}

目录

说明

函数声明 类似,函数的返回类型(由 说明符与限定符 中的类型说明符确定,并可能像 声明 中通常那样被 声明符 修改)必须是完整的非数组对象类型或 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 个整数的数组的指针
    return malloc(sizeof(int[3]));
}

函数声明 类似,在构建函数类型时,参数类型会从函数类型调整为指针类型、从数组类型调整为指针类型,并且所有参数类型的顶层cv限定符在确定 兼容函数类型 时将被忽略。

函数声明 不同,不允许存在未命名的形参(否则会与旧式(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; } // 正确:void 不声明参数

在函数体内,所有具名参数都是 左值 表达式,它们具有自动 存储期 块作用域 。参数在内存中的布局(或是否存储在内存中)是未指定的:这是 调用约定 的一部分。

int main(int ac, char **av)
{
    ac = 2; // 参数是左值
    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标准中, 说明符与限定符 是可选的,若省略则函数返回类型默认为 int (可能被 声明符 修正)。

此外,旧式定义不要求 声明列表 中包含每个参数的声明。未声明参数的类型默认为 int

max(a, b) // a和b具有int类型,返回类型为int
{
    return a>b?a:b;
}
(C99前)

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的C语言标准。

缺陷报告 适用范围 发布时行为 正确行为
DR 423 C89 返回类型可能带有限定符 返回类型被隐式解除限定

参考文献

  • C17 标准 (ISO/IEC 9899:2018):
  • 6.9.1 函数定义 (p: 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++ 文档 关于 函数定义