命名空间
变体
操作

声明

来自 cppreference.com
< c‎ | 语言

一个声明是 C 语言结构,它将一个或多个标识符引入程序,并指定它们的含义和属性。

声明可以出现在任何作用域中。每个声明以分号结尾(就像语句一样),并由(直到 C23)(自 C23 起)个不同的部分组成

说明符和限定符 声明符和初始化器(可选) ; (1)
属性说明符序列 说明符和限定符 声明符和初始化器 ; (2) (自 C23 起)
属性说明符序列 ; (3) (自 C23 起)

其中

说明符和限定符 - 空格分隔的列表,以任意顺序,
  • 类型说明符
声明符和初始化器 - 由逗号分隔的声明符列表(每个声明符提供附加的类型信息和/或要声明的标识符)。声明符可以伴随初始化器枚举结构体联合体 声明可以省略声明符,在这种情况下,它们只引入枚举常量和/或标签。
属性说明符序列 - (C23)可选的属性列表,应用于已声明的实体,或者如果单独出现,则形成属性声明。
1-2) 简单声明。引入一个或多个标识符,表示对象、函数、结构体/联合体/枚举标签、类型定义或枚举常量。
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 声明为指向由 S 决定的类型的cvr 限定指针。
4) 数组声明符:声明 S D[N]D 声明为由 S 决定的类型的 N 个对象的数组。非指针声明符 是除未用括号括起来的指针声明符之外的任何其他声明符。
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 起) 的可选序列。当出现在标识符之后时,它应用于正在声明的对象或函数。

[编辑] 定义

一个定义是提供其声明的标识符的所有信息的声明。

每个枚举类型定义的声明都是一个定义。

对于函数,包含函数体的声明是一个函数定义

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

对于对象,分配存储空间(自动的或静态的,但不是外部的)的声明是一个定义,而没有分配存储空间(外部声明)的声明不是。

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

对于结构体联合体,指定成员列表的声明是定义

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 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)

静态断言 从 C 语法的角度来看被认为是声明(因此它们可以出现在任何声明可以出现的地方),但它们不引入任何标识符,也不遵循声明语法。

(自 C11)

属性 声明也被认为是声明(因此它们可以出现在任何声明可以出现的地方),但它们不引入任何标识符。单个 ; 且没有 attr-spec-seq 不是属性声明,而是一个语句。

(自 C23 起)

[编辑] 参考

  • 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++ 文档 声明