命名空间
变体
操作

函数定义

来自 cppreference.com
< c‎ | 语言

函数定义将函数体(一系列声明和语句)与函数名称和参数列表关联。与函数声明不同,函数定义只能在文件范围内(没有嵌套函数)。

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); } // return type is void
int sum(int a, int b) { return a+b; } // return type is int
int (*foo(const void *p))[3] { // return type is pointer to array of 3 int
    return malloc(sizeof(int[3]));
}

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

函数声明不同,不允许使用无名形式参数(否则,在老式 (K&R) 函数定义中会发生冲突),即使它们不在函数中使用,也必须命名它们。唯一的例外是特殊参数列表(void).

(直到 C23)

形式参数可以在函数定义中是无名的,因为老式 (K&R) 函数定义已被删除。无名参数在函数体中是无法通过名称访问的。

(自 C23 起)
int f(int, int); // declaration
// int f(int, int) { return 7; } // Error until C23, OK since C23
int f(int a, int b) { return 7; } // definition
int g(void) { return 8; } // OK: void doesn't declare a parameter

在函数体中,每个命名的参数都是一个左值表达式,它们具有自动存储持续时间块范围。参数在内存中的布局(或者它们是否完全存储在内存中)是未指定的:它是调用约定的一部分。

int main(int ac, char **av)
{
    ac = 2; // parameters are lvalues
    av = (char *[]){"abc", "def", NULL};
    f(ac, av);
}

有关函数调用机制的更多详细信息,请参阅函数调用运算符,有关从函数返回的详细信息,请参阅return

__func__

在每个function-body中,都提供了具有块范围和静态存储持续时间的特殊预定义变量__func__,就好像在第一个大括号后立即通过以下方式定义一样

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

此特殊标识符有时与预定义宏常量__FILE____LINE__组合使用,例如,由assert

(自 C99 起)

[编辑] 注意

参数列表必须在声明符中明确存在,不能从 typedef 继承。

typedef int p(int q, int r); // p is a function type int(int, int)
p f { return q + r; } // Error

在 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 应用于 发布的行为 正确的行为
DR 423 C89 返回类型可能被限定 返回类型隐式地被取消限定

[编辑] 参考

  • 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++ 文档 用于 函数定义