命名空间
变体
操作

函数声明

来自 cppreference.cn
< C‎ | 语言

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

目录

[编辑] 语法

在函数声明的声明文法中,类型说明符序列(可能被声明符修饰)指定返回类型(可以是除数组或函数类型之外的任何类型),并且声明符具有以下三种形式之一

noptr-声明符 ( 参数列表 ) attr-spec-seq(可选) (1)
noptr-声明符 ( 标识符列表 ) attr-spec-seq(可选) (2) (直到 C23)
noptr-声明符 ( ) attr-spec-seq(可选) (3)

其中

noptr-声明符 - 任何声明符,但未加括号的指针声明符除外。此声明符中包含的标识符是成为函数指示符的标识符。
参数列表 - 可以是单个关键字 void 或逗号分隔的参数列表,该列表可以以省略号参数结尾
标识符列表 - 逗号分隔的标识符列表,仅当此声明符用作旧式函数定义的一部分时才可能
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)等效于参数列表 void 的新式函数声明(自 C23 起)

[编辑] 解释

函数的返回类型,由specifiers-and-qualifiers中的类型说明符确定,并可能像声明中通常那样被声明符修饰,必须是非数组对象类型或 void 类型。如果函数声明不是定义,则返回类型可以是不完整的。返回类型不能是 cvr 限定的:任何限定的返回类型都会调整为其非限定版本,以用于构造函数类型。

void f(char *s);                    // return type is void
int sum(int a, int b);              // return type of sum is int.
int (*foo(const void *p))[3];       // return type is pointer to array of 3 int
 
double const bar(void);             // declares function of type double(void)
double (*barp)(void) = bar;         // OK: barp is a pointer to double(void)
double const (*barpc)(void) = barp; // OK: barpc is also a pointer to double(void)

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

int f(void), *fip(), (*pfi)(), *ap[3]; // declares two functions and two objects
inline int g(int), n; // Error: inline qualifier is for functions only
typedef int array_t[3];
array_t a, h(); // Error: array type cannot be a return type for a function

如果函数声明出现在任何函数之外,则它引入的标识符具有文件作用域外部链接,除非使用 static 或较早的静态声明可见。如果声明发生在另一个函数内部,则标识符具有块作用域(以及内部或外部链接)。

int main(void)
{
    int f(int); // external linkage, block scope
    f(1); // definition needs to be available somewhere in the program
}

声明中的参数(不是函数定义的一部分)(直到 C23)不需要命名

int f(int, int); // declaration
// int f(int, int) { return 7; } // Error: parameters must be named in definitions
// This definition is allowed since C23

参数列表中的每个参数都是一个声明,它引入了一个单变量,具有以下附加属性

  • 声明符中的标识符是可选的(除非此函数声明是函数定义的一部分)(直到 C23)
int f(int, double); // OK
int g(int a, double b); // also OK
// int f(int, double) { return 1; } // Error: definition must name parameters
// This definition is allowed since C23
  • 参数允许的唯一存储类说明符register,并且在非定义的函数声明中会被忽略
int f(static int x); // Error
int f(int [static 10]); // OK (array index static is not a storage class specifier)
  • 任何数组类型的参数都会调整为相应的指针类型,如果数组声明符的方括号之间存在限定符,则该指针类型可能是限定的(自 C99 起)
int f(int[]); // declares int f(int*)
int g(const int[10]); // declares int g(const int*)
int h(int[const volatile]); // declares int h(int * const volatile)
int x(int[*]); // declares int x(int*)
  • 任何函数类型的参数都会调整为相应的指针类型
int f(char g(double)); // declares int f(char (*g)(double))
int h(int(void)); // declares int h(int (*)(void))
  • 参数列表可以以 , ... 结尾或为 ...(自 C23 起),有关详细信息,请参见可变参数函数
int f(int, ...);
  • 参数不能具有 void 类型(但可以具有指向 void 的指针类型)。完全由关键字 void 组成的特殊参数列表用于声明不带参数的函数。
int f(void); // OK
int g(void x); // Error
  • 参数列表中出现的任何可以被视为 typedef 名称或参数名称的标识符都将被视为 typedef 名称:int f(size_t, uintptr_t) 被解析为新式声明符,用于接受两个未命名参数(类型为 size_t 和 uintptr_t)的函数,而不是旧式声明符,它开始定义一个接受两个名为“size_t”和“uintptr_t”的参数的函数
  • 参数可能具有不完整类型并且可以使用 VLA 表示法 [*](自 C99 起)(除非在函数定义中,数组到指针和函数到指针调整后的参数类型必须是完整的)

属性说明符序列也可以应用于函数参数。

(自 C23 起)

有关函数调用的机制和其他详细信息,请参见函数调用运算符,有关从函数返回的信息,请参见返回

[编辑] 注释

与 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
(直到 C23)

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

typedef int p(int q, int r); // p is a function type int(int, int)
p f; // declares int f(int, int)

在 C89 中,specifiers-and-qualifiers 是可选的,如果省略,则函数的返回类型默认为 int(可能由 声明符 修改)。

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

[编辑] 缺陷报告

以下行为变更缺陷报告已追溯应用于以前发布的 C 标准。

DR 应用于 已发布行为 正确行为
DR 423 C89 返回类型可能是限定的 返回类型是隐式取消限定的

[编辑] 参考文献

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

[编辑] 参见

C++ 文档 关于 函数声明