Namespaces
Variants

Function declarations

From cppreference.net

函数声明引入一个 标识符 ,该标识符用于指代函数,并可选择性地指定函数参数的类型(即 原型 )。函数声明(与 定义 不同)可以出现在块作用域和文件作用域中。

目录

语法

在函数声明的 声明语法 中,可能被声明符修改的 类型说明符 序列指定了 返回类型 (可以是除数组或函数类型之外的任何类型),而 声明符 具有 三种 (C23前) 两种 (C23起) 形式之一:

noptr-declarator ( parameter-list ) attr-spec-seq (optional) (1)
noptr-declarator ( identifier-list ) attr-spec-seq (optional) (2) (C23前)
noptr-declarator ( ) attr-spec-seq (optional) (3)

其中

noptr-declarator - 除未加括号的指针声明符之外的任何 声明符 。该声明符中包含的标识符即为成为函数指示符的标识符。
parameter-list - 可以是单独的关键字 void ,也可以是逗号分隔的 形参 列表(可能以 可变参数 结尾)
identifier-list - 逗号分隔的标识符列表,仅当该声明符作为旧式 函数定义 的一部分时可用
attr-spec-seq - (C23) 可选的 属性 列表,应用于函数类型
1) 新式(C89风格)函数声明。该声明既引入了函数指示符本身,也作为后续任何 函数调用表达式 的函数原型,强制将实参表达式转换为声明的形参类型,并对实参数量进行编译时检查。
int max(int a, int b); // declaration
int n = max(12.01, 3.14); // OK, conversion from double to int
2) (C23前) 旧式(K&R)函数定义。此声明不会引入函数原型,后续所有 函数调用表达式 将执行默认参数提升,且当实参数量与形参数量不匹配时将引发未定义行为。
int max(a, b)
    int a, b; // definition expects ints; the second call is undefined
{
    return a > b ? a : b;
}
int n = max(true, (char)'a'); // calls max with two int args (after promotions)
int n = max(12.01f, 3.14); // calls max with two double args (after promotions)
3) 非原型函数声明。此声明不会引入原型 (C23前) 等效于形参列表为 parameter-list void 的新风格函数声明 (C23起)

说明

函数的返回类型由 说明符与限定符 中的类型说明符决定,并可能按照 声明 中的常规方式被 声明符 修改,必须是非数组对象类型或 void 类型。若函数声明非定义式,则返回类型可以是 不完整类型 。返回类型不能带有cv限定符:任何带有限定符的返回类型在构建函数类型时,都会被调整为无限定版本。

void f(char *s);                    // 返回类型为 void
int sum(int a, int b);              // sum 的返回类型为 int
int (*foo(const void *p))[3];       // 返回类型为指向 3 个 int 数组的指针
double const bar(void);             // 声明函数类型为 double(void)
double (*barp)(void) = bar;         // 正确:barp 是指向 double(void) 的指针
double const (*barpc)(void) = barp; // 正确:barpc 也是指向 double(void) 的指针

函数声明符可以与其他声明符组合,只要它们能够共享其类型说明符和限定符

int f(void), *fip(), (*pfi)(), *ap[3]; // 声明两个函数和两个对象
inline int g(int), n; // 错误:inline限定符仅适用于函数
typedef int array_t[3];
array_t a, h(); // 错误:数组类型不能作为函数的返回类型

若函数声明出现在任何函数外部,其引入的标识符具有 文件作用域 外部链接 ,除非使用了 static 关键字或存在可见的早期静态声明。若声明出现在其他函数内部,该标识符具有块作用域(同时具有内部或外部链接)。

int main(void)
{
    int f(int); // 外部链接,块作用域
    f(1); // 函数定义需要在程序中的某处可用
}

不属于 函数定义 部分的 (C23前) 声明中,参数无需命名:

int f(int, int); // 声明
// int f(int, int) { return 7; } // 错误:定义中的参数必须命名
// 此定义自 C23 起被允许

函数 参数列表 中的每个参数都是一个 声明 ,用于引入单个变量,并具有以下附加属性:

  • 声明符中的标识符是可选的 (除非此函数声明是函数定义的一部分) (C23 前)
int f(int, double); // 正确
int g(int a, double b); // 同样正确
// int f(int, double) { return 1; } // 错误:定义必须命名参数
// 此定义自 C23 起被允许
  • 函数参数唯一允许的 存储类说明符 register ,并且在非定义的函数声明中该说明符会被忽略
int f(static int x); // 错误
int f(int [static 10]); // 正确(数组索引的 static 不是存储类说明符)
  • 任何数组类型的参数都会被调整为对应的指针类型 ,如果数组声明符的方括号之间存在限定符,则该指针类型可能被限定 (C99 起)
int f(int[]); // 声明 int f(int*)
int g(const int[10]); // 声明 int g(const int*)
int h(int[const volatile]); // 声明 int h(int * const volatile)
int x(int[*]); // 声明 int x(int*)
  • 函数类型的任何形参都会被调整为对应的指针类型
int f(char g(double)); // 声明 int f(char (*g)(double))
int h(int(void)); // 声明 int h(int (*)(void))
int f(int, ...);
  • 参数不能具有 void 类型(但可以具有指向 void 的指针类型)。完全由关键字 void 组成的特殊参数列表用于声明不接收任何参数的函数。
int f(void); // 正确
int g(void x); // 错误
  • 任何出现在参数列表中且可被视作类型定义名或参数名的标识符,均被视为类型定义名: int f ( size_t , uintptr_t ) 会被解析为接收两个未命名参数(类型分别为 size_t uintptr_t )的新式函数声明符,而非开启函数定义、接收两个名为 " size_t " 和 " uintptr_t " 参数的旧式声明符。
  • 参数可以具有不完整类型 并可使用 VLA 表示法 [ * ] (C99 起) (但在 函数定义 中,经过数组到指针和函数到指针调整后的参数类型必须是完整类型)。

属性说明符序列 同样可应用于函数参数。

(since C23)

有关函数调用机制的其他细节,请参阅 函数调用运算符 ,关于从函数返回的内容请参见 return 说明。

注释

与C++不同,声明符 f ( ) f ( void ) 具有不同的含义:声明符 f ( void ) 是新式(原型)声明符,用于声明不接受参数的函数。声明符 f ( ) 是声明符,用于声明接受 未指定 数量参数的函数(除非用于函数定义中)

int f(void); // declaration: takes no parameters
int g(); // declaration: takes unknown parameters
int main(void) {
    f(1); // compile-time error
    g(2); // undefined behavior
}
int f(void) { return 1; } // actual definition
int g(a,b,c,d) int a,b,c,d; { return 2; } // actual definition
(until C23)

函数定义 不同,参数列表可以从 typedef 继承

typedef int p(int q, int r); // p 是函数类型 int(int, int)
p f; // 声明 int f(int, int)

在C89中, 说明符与限定符 是可选的,若省略则函数返回类型默认为 int (可能被 声明符 修正)。

*f() { // function returning int*
   return NULL;
}
(C99前)

缺陷报告

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

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

参考文献

  • C23 标准 (ISO/IEC 9899:2024):
  • 6.7.7.4 函数声明符(包括原型)(p: 130-132)
  • C17 标准 (ISO/IEC 9899:2018):
  • 6.7.6.3 函数声明符(含原型)(页码: 96-98)
  • C11 标准 (ISO/IEC 9899:2011):
  • 6.7.6.3 函数声明符(包含原型)(p: 133-136)
  • C99标准(ISO/IEC 9899:1999):
  • 6.7.5.3 函数声明符(含原型)(第118-121页)
  • C89/C90 标准 (ISO/IEC 9899:1990):
  • 3.5.4.3 函数声明符(包含原型)

参见

C++ 文档 关于 函数声明