声明
声明 是 C 语言构造,它将一个或多个 标识符 引入程序,并指定它们的含义和属性。
声明可以出现在任何作用域中。每个声明都以分号结尾(就像 语句 一样),并且由 两个(直到 C23)三个(自 C23 起) 不同的部分组成
说明符和限定符 声明符和初始化器 (可选) ; |
(1) | ||||||||
属性说明序列 说明符和限定符 声明符和初始化器 ; |
(2) | (自 C23 起) | |||||||
属性说明序列 ; |
(3) | (自 C23 起) | |||||||
其中
说明符和限定符 | - | 空格分隔的列表,顺序任意,包括:
|
声明符和初始化器 | - | 逗号分隔的 声明符 列表(每个声明符提供附加的类型信息和/或要声明的标识符)。声明符可以伴随 初始化器。enum、struct 和 union 声明可以省略 声明符,在这种情况下,它们仅引入枚举常量和/或标签。 |
属性说明序列 | - | (C23)属性的可选列表,应用于声明的实体,或者如果单独出现,则形成属性声明。 |
例如,
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) | ||||||||
这种语法的理由是,当声明符声明的标识符以与声明符相同的形式出现在表达式中时,它将具有由类型说明符序列指定的类型。
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 起)。当紧跟在标识符之后出现时,它应用于正在声明的对象或函数。
[编辑] 定义
定义 是提供有关其声明的标识符的所有信息的声明。
对于函数,包含函数体的声明是 函数定义
int foo(double); // declaration int foo(double x) { return x; } // definition
对于对象,分配存储(自动或静态,但不是 extern)的声明是定义,而不分配存储(外部声明)的声明不是定义。
extern int n; // declaration int n = 10; // definition
对于 struct 和 union,指定成员列表的声明是定义
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 类型的使用还有其他限制,请参阅 goto、switch。longjmp |
(自 C99 起) |
static_asserts 从 C 语法角度来看被认为是声明(因此它们可以出现在声明可以出现的任何地方),但它们不引入任何标识符,也不遵循声明语法。 |
(自 C11 起) |
属性声明也被认为是声明(因此它们可以出现在声明可以出现的任何地方),但它们不引入任何标识符。单个 |
(自 C23 起) |
[编辑] 参考文献
- C23 标准 (ISO/IEC 9899:2024)
- 6.7 声明 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018)
- 6.7 声明 (p: 78-105)
- C11 标准 (ISO/IEC 9899:2011)
- 6.7 声明 (p: 108-145)
- C99 标准 (ISO/IEC 9899:1999)
- 6.7 声明 (p: 97-130)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.5 声明
[编辑] 参见
C++ 文档 关于 声明
|