C
C 语法

Declarations

声明

声明是C语言结构,介绍一个或多个标识符到程序并指定它们的含义和性质。

声明可能出现在任何范围内。每个声明以分号结尾(就像声明一样),由两个不同的部分组成:

specifiers-and-qualifiers declarators-and-initializers ;

其中

符和限定符-以任何顺序,以空格分隔的列表类型说明符:void算术类型的名称原子类型的名称以前由typedef声明引入的名称struct,union或enum说明符零个或一个存储类说明符:typedef ,auto,register,static,extern,thread_local零个或多个类型限定符:const,volatile,restrict,_Atomic(仅在声明函数时),零个或多个函数限定符:inline,noreturn零个或多个对齐指定符:alignas
声明符-和初始化-用逗号分隔的声明者列表(每个声明者提供附加的类型信息和/或声明的标识符)。声明者可能伴随着初始值。枚举,结构和联合声明可以省略声明符,在这种情况下,它们只引入枚举常量和/或标记。

  • 类型说明符:

declarators-and-initializers - comma-separated list of declarators (each declarator provides additional type information and/or the identifier to declare). Declarators may be accompanied by [initializers](initialization). The [enum](enum), [struct](struct), and [union](union) declarations may omit declarators, in which case they only introduce the enumeration constants and/or tags.

例如,

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

声明中引入的每个标识符的类型由类型说明符指定的类型和其声明者应用的类型修改组合确定。

声明符

每个声明符是以下之一:

identifier(1)
( declarator )(2)
* qualifiers(optional) declarator(3)
noptr-declarator static(optional) qualifiers(optional) expression noptr-declarator qualifiers(optional) * (4)
noptr-declarator ( parameters-or-identifiers )(5)

1)这个声明者介绍的标识符。

2)任何申报人可以用括号括起来; 这是需要引入指向数组的指针和指向函数的指针。

3)指针声明符:声明S * cvr D; 声明D为由cvr限定的类型的指针S

4)数组声明符:声明S D[N]声明为由D数组N确定的类型的对象S。noptr-declarator 是除 unparenthesized 指针声明符之外的任何其他声明符。

5)函数声明符:将声明S D(params)声明D为一个带参数params并返回的函数S。noptr-declarator 是除 unparenthesized 指针声明符之外的任何其他声明符。

这种语法背后的原因是,当由声明者声明的标识符出现在与声明者相同形式的表达式中时,它将具有由类型说明符序列指定的类型。

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];

每个不属于另一个声明符的声明符的结尾都是一个序列点。

定义

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

每个枚举或 typedef 的声明都是一个定义。

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

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

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

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 可以重复,只要它命名相同的类型即可:

typedef int int_t; typedef int int_t; // OK

  • 结构和联合声明可以重复:

struct X; struct X { int n; }; struct X;

这些规则简化了头文件的使用。

注意

在C89中,在任何语句之前,任何复合语句(块范围)内的声明都必须出现在块的开头。另外,在C89中,返回int的函数可以由函数调用操作符隐式声明,并且在使用旧式函数定义时不必声明int类型的函数参数。(直到C99)

空宣布者是被禁止的; 一个声明必须是一个 static_assert 声明或者(因为C11)至少有一个声明器或声明至少一个 struct / union / enum 标记,或者至少引入一个枚举常量。

如果声明符的任何部分是 VLA 数组声明符,则整个声明符的类型称为“可变更改类型”。从可变修改类型定义的类型也是可变修改的(VM)。任何可变修改类型的声明可能只出现在块作用域或函数原型范围内,并且不能是结构体或联合体的成员。尽管 VLA 只能具有自动存储持续时间,但 VM 类型(例如指向VLA的指针)可能是静态的。使用 VM 类型还有其他限制,请参阅转到,切换。longjmp 的。(自C99以来)

从C语法的角度来看,static_assert被认为是声明(这样它们可以出现在声明的任何地方),但是它们不引入任何标识符,也不遵循声明语法。(自C11以来)

参考

  • C11 standard (ISO/IEC 9899:2011):