函式宣告
來自 cppreference.com
函式宣告引入一個識別符號,用於指定一個函式,並可選地指定函式引數的型別(即原型)。函式宣告(與函式定義不同)可以出現在塊作用域以及檔案作用域中。
目錄 |
[編輯] 語法
在函式宣告的宣告語法中,型別說明符序列(可能由宣告符修改)指定返回型別(可以是除陣列或函式型別以外的任何型別),並且宣告符有以下三種形式之一:
noptr-declarator ( parameter-list ) attr-spec-seq(可選) |
(1) | ||||||||
noptr-declarator ( identifier-list ) attr-spec-seq(可選) |
(2) | (直至 C23) | |||||||
noptr-declarator ( ) attr-spec-seq(可選) |
(3) | ||||||||
其中
noptr-declarator | - | 任何宣告符,除了未加括號的指標宣告符。此宣告符中包含的識別符號成為函式指定符。 |
parameter-list | - | 單個關鍵字void或逗號分隔的引數列表,列表末尾可以是一個省略號引數 |
identifier-list | - | 逗號分隔的識別符號列表,僅當此宣告符用作舊式函式定義的一部分時才可能出現 |
屬性說明序列 | - | (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 起)。
[編輯] 解釋
函式的返回型別,由說明符和限定符中的型別說明符確定,並可能像宣告中通常一樣由宣告符修改,必須是非陣列物件型別或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 起) |
關於函式呼叫機制的其他細節,請參見函式呼叫運算子;關於從函式返回,請參見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 |
(直至 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 中,說明符和限定符是可選的,如果省略,函式的返回型別預設為int(可能由宣告符修改)。 *f() { // function returning int* return NULL; } |
(直到 C99) |
[編輯] 缺陷報告
以下行為改變的缺陷報告被追溯地應用於以前釋出的 C 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
DR 423 | C89 | 返回型別可能被限定 | 返回型別被隱式取消限定 |