名稱空間
變體
操作

宣告

來自 cppreference.com
< c‎ | language

宣告是 C 語言的一種構造,它向程式中引入一個或多個識別符號,並指定它們的含義和屬性。

宣告可以出現在任何作用域中。每個宣告都以分號結尾(就像語句一樣),並由(C23 前)(C23 起)個不同的部分組成

說明符和限定符 宣告符和初始化器 (可選) ; (1)
屬性說明符序列 說明符和限定符 宣告符和初始化器 ; (2) (自 C23 起)
屬性說明符序列 ; (3) (自 C23 起)

其中

說明符和限定符 - 空格分隔的列表,順序任意:
  • 型別說明符


宣告符和初始化器 - 逗號分隔的宣告符列表(每個宣告符提供額外的型別資訊和/或要宣告的識別符號)。宣告符可以伴隨初始化器enumstructunion 宣告可以省略宣告符,在這種情況下,它們只引入列舉常量和/或標籤。
屬性說明序列 - (C23)可選的屬性列表,應用於已宣告的實體,如果單獨出現則構成屬性宣告。
1,2) 簡單宣告。引入一個或多個識別符號,它們表示物件、函式、結構/聯合/列舉標籤、typedefs 或列舉常量。
3) 屬性宣告。不宣告任何識別符號,如果含義未由標準指定,則具有實現定義的含義。

例如,

int a, *b=NULL; // "int" is the type specifier,
                // "a" is a declarator
                // "*b" is a declarator and NULL is its initializer
const int *f(void); // "int" is the type specifier
                    // "const" is the type qualifier
                    // "*f(void)" is the declarator
enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" is the type specifier
                                 // "c" is the declarator

宣告中引入的每個識別符號的型別由型別說明符指定的型別與透過其宣告符應用的型別修改的組合決定。如果使用 auto 說明符,變數的型別也可以被推斷。(C23 起)

屬性(C23 起)可以出現在說明符和限定符中,在這種情況下,它們適用於由前面說明符確定的型別。

目錄

[編輯] 宣告符

每個宣告符都是以下之一

識別符號 屬性說明符序列 (可選) (1)
( 宣告符 ) (2)
* 屬性說明符序列 (可選) 限定符 (可選) 宣告符 (3)
非指標宣告符 [ static(可選) 限定符 (可選) 表示式 ]

非指標宣告符 [ 限定符 (可選) * ]

(4)
非指標宣告符 ( 引數或識別符號 ) (5)
1) 此宣告符引入的識別符號。
2) 任何宣告符都可以用括號括起來;這是宣告陣列指標和函式指標所必需的。
3) 指標宣告符:宣告 S * cvr D; 將 D 宣告為 cvr-限定的指向由 S 確定的型別的指標。
4) 陣列宣告符:宣告 S D[N]D 宣告為 N 個由 S 確定的型別的物件的陣列。非指標宣告符是除未加括號的指標宣告符之外的任何其他宣告符。
5) 函式宣告符:宣告 S D(params)D 宣告為接受引數 params 並返回 S 的函式。非指標宣告符是除未加括號的指標宣告符之外的任何其他宣告符。

此語法背後的原理是,當宣告符宣告的識別符號出現在與宣告符形式相同的表示式中時,它將具有型別說明符序列指定的型別。

struct C
{
    int member; // "int" is the type specifier
                // "member" is the declarator
} obj, *pObj = &obj;
// "struct C { int member; }" is the type specifier
// declarator "obj" defines an object of type struct C
// declarator "*pObj" declares a pointer to C,
// initializer "= &obj" provides the initial value for that pointer
 
int a = 1, *p = NULL, f(void), (*pf)(double);
// the type specifier is "int"
// declarator "a" defines an object of type int
//   initializer "=1" provides its initial value
// declarator "*p" defines an object of type pointer to int
//   initializer "=NULL" provides its initial value
// declarator "f(void)" declares a function taking void and returning int
// declarator "(*pf)(double)" defines an object of type pointer
//   to function taking double and returning int
 
int (*(*foo)(double))[3] = NULL;
// the type specifier is int
// 1. declarator "(*(*foo)(double))[3]" is an array declarator:
//    the type declared is "/nested declarator/ array of 3 int"
// 2. the nested declarator is "*(*foo)(double))", which is a pointer declarator
//    the type declared is "/nested declarator/ pointer to array of 3 int"
// 3. the nested declarator is "(*foo)(double)", which is a function declarator
//    the type declared is "/nested declarator/ function taking double and returning
//        pointer to array of 3 int"
// 4. the nested declarator is "(*foo)" which is a (parenthesized, as required by
//        function declarator syntax) pointer declarator.
//    the type declared is "/nested declarator/ pointer to function taking double
//        and returning pointer to array of 3 int"
// 5. the nested declarator is "foo", which is an identifier.
// The declaration introduces the identifier "foo" to refer to an object of type
// "pointer to function taking double and returning pointer to array of 3 int"
// The initializer "= NULL" provides the initial value of this pointer.
 
// If "foo" is used in an expression of the form of the declarator, its type would be
// int.
int x = (*(*foo)(1.2))[0];

每個不屬於另一個宣告符的宣告符的末尾都是一個序列點

在所有情況下,屬性說明符序列屬性(C23 起)的可選序列。當它緊跟在識別符號之後時,它應用於被宣告的物件或函式。

[編輯] 定義

定義是提供其宣告的識別符號的所有資訊的宣告。

每個 enumtypedef 的宣告都是定義。

對於函式,包含函式體的宣告是函式定義

int foo(double); // declaration
int foo(double x) { return x; } // definition

對於物件,分配儲存(自動或靜態,但不是 extern)的宣告是定義,而未分配儲存(外部宣告)的宣告則不是。

extern int n; // declaration
int n = 10; // definition

對於 structsunions,指定成員列表的宣告是定義

struct X; // declaration
struct X { int n; }; // definition

[編輯] 重宣告

如果同一作用域中該識別符號的另一個宣告早於此宣告,則宣告不能引入識別符號,除非

  • 可以重複宣告具有連結(外部或內部)的物件
extern int x;
int x = 10; // OK
extern int x; // OK
 
static int n;
static int n = 10; // OK
static int n; // OK
  • 只要非 VLA typedef 命名同一型別,就可以重複
typedef int int_t;
typedef int int_t; // OK
struct X;
struct X { int n; };
struct X;

這些規則簡化了標頭檔案的使用。

[編輯] 注意

在 C89 中,任何複合語句(塊作用域)中的宣告必須出現在塊的開頭,在任何語句之前。

此外,在 C89 中,返回 int 的函式可以由函式呼叫運算子隱式宣告,並且在使用舊式函式定義時,型別為 int 的函式引數無需宣告。

(直到 C99)

禁止空宣告符;簡單宣告必須至少有一個宣告符或宣告至少一個結構/聯合/列舉標籤,或引入至少一個列舉常量。

如果宣告符的任何部分是變長陣列 (VLA) 宣告符,則整個宣告符的型別稱為“可變修改型別”。由可變修改型別定義的型別也是可變修改 (VM) 型別。

任何可變修改型別的宣告只能出現在塊作用域或函式原型作用域中,並且不能是結構或聯合的成員。儘管 VLA 只能具有自動或已分配的儲存持續時間,但 VM 型別(例如指向 VLA 的指標)可以是靜態的。使用 VM 型別還有其他限制,請參閱 gotoswitchlongjmp

(C99 起)

static_asserts 從 C 語法角度來看被認為是宣告(因此它們可以出現在宣告可以出現的任何地方),但它們不引入任何識別符號,也不遵循宣告語法。

(C11 起)

屬性宣告也被認為是宣告(因此它們可以出現在宣告可以出現的任何地方),但它們不引入任何識別符號。沒有屬性說明符序列的單個;不是屬性宣告,而是語句。

(自 C23 起)

[編輯] 參考

  • C23 標準 (ISO/IEC 9899:2024)
  • 6.7 Declarations (p: 待定)
  • C17 標準 (ISO/IEC 9899:2018)
  • 6.7 Declarations (p: 78-105)
  • C11 標準 (ISO/IEC 9899:2011)
  • 6.7 Declarations (p: 108-145)
  • C99 標準 (ISO/IEC 9899:1999)
  • 6.7 Declarations (p: 97-130)
  • C89/C90 標準 (ISO/IEC 9899:1990)
  • 3.5 Declarations

[編輯] 另請參閱

C++ 文件,關於宣告