命名空间
变体
操作

声明

来自 cppreference.cn
< 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++ 文档,关于声明