声明(一)
一、概述
声明规定了标识符的解释和属性。C语言中存在以下四种语法格式的声明:
declaration-specifiers init-declarator-listopt;
attribute-specifier-sequence declaration-specifiers init-declarator-list;
static_assert-declaration
attribute-declaration
declaration-specifiers是声明说明符,包括存储类说明符、类型限定符、函数说明符。声明说明符由一个说明符序列和一个可选的属性说明符序列组成。如果存在多个声明说明符,声明说明符之间使用空格分隔。声明说明符指定了声明符表示实体的链接属性、存储期限和部分类型。
init-declarator-list是初始声明符列表。初始声明符列表是以逗号分隔的声明符序列,每个声明符都可以包含附加类型信息或者初始化器,或者两者兼而有之。声明符包含被声明的标识符(如果存在。)。
attribute-specifier-sequence是属性说明符序列。声明中可选的属性说明符序列适用于初始声明符列表中声明符声明的实体。
static_assert-declaration是静态断言声明,静态断言声明存在以下两种语法格式:
static_assert(constant-expression, string-literal); static_assert(constant-expression);
其中constant-expression是常量表达式,string-literal是字符串字面量。
attribute-declaration是属性声明。除非另有规定,否则属性声明的含义将由实现定义。
对于无链接对象标识符,对象类型在声明符结束时应是完整类型;如果存在初始化器,在初始声明符结束时应是完整类型;如果是函数的形式参数,要求转换后的类型是完整类型。
int i; //标识符i类型是int类型。 char arr[] = "China"; //标识符arr类型是char [6]类型。 int func(int n, int a[n]); //标识符func类型是int (*)(int, int *)类型。
终止声明说明符序列的可选属性说明符序列适用于前一个声明说明符序列所确定的类型。属性说明符序列只影响其出现的声明中的类型,而不会影响涉及同一类型的其它声明。
声明时,与声明实体相关联的属性可以出现在声明开头,也可以出现在声明标识符后。
[[gnu::const]] [[nodiscard]] int f1(int); [[gnu::const]] int f2 [[nodiscard]](int);
其中[[gnu::const]]是GCC编译器支持的属性;[[nodiscard]]是标准属性。
标识符定义是特殊的标识符声明;对于对象,定义会为对象分配存储空间;对于函数,定义将包含函数体;对于枚举常量,定义是标识符的第一次(或者仅有的)声明;对于typedef名,定义是标识符的第一次(或者仅有的)声明。
extern int a; //是声明,不是定义,不会给a分配存储空间。 int b; //是声明,也是定义,会给b分配存储空间。
使用extern存储类说明符声明对象,仅是声明,不是定义;声明的对象应在其它位置定义。
如果声明说明符不包含类型说明符或者声明中使用了constexpr存储类说明符,则称为未充分指定声明(underspecified declaration)。如果未充分指定声明不是定义,或者如果未充分指定声明没有声明或者声明了一个以上的普通标识符,或者如果未充分指定声明声明的标识符在同一作用域内已声明,或者如果未充分指定声明声明的实体不是对象,或者如果在构成未充分指定声明的标记序列的任何地方声明了非普通标识符,那么其行为将由实现定义。ISO/IEC C标准建议接受此类声明的实现遵循ISO/IEC 14882标准中相关语义。
auto a = 3, b = 5; //实现定义行为。
不包含类型说明符的声明是未充分指定声明,并且声明了两个普通标识符,所以上述声明具有实现定义行为。
如果声明(static_assert声明、属性声明除外)不包含初始声明符列表,则声明说明符应包含以下内容之一:
-- 包含标记的结构说明符、联合说明符或者枚举说明符,用于声明标记。
enum e : unsigned; struct s; union u;
-- 包含枚举器列表的枚举说明符。
enum color {black, white};
如果声明的标记或者枚举常量位于嵌套构造中,因为不具有声明说明符指定的语法格式,所以是非法的。
struct s{ int i; }; //非法。标记s2在嵌套构造中声明。 struct s1{ struct s2{int i} a; }; //合法。 struct s3{ struct s a; };
如果标识符无链接,在相同作用域、相同命名空间内不能存在多个该标识符声明(在声明符或者类型说明符中。),但以下情况除外:
-- 如果类型不是可变修改类型(variably modified type),可重定义typedef名以表示与当前类型相同的类型。
(注:如果一个声明符包含变长数组(variable-length array),那么这个声明符的类型是可变修改类型。)
typedef int Integer; typedef signed Integer;
-- 枚举常量和标记可以重新声明。
struct s; //声明结构标记s,这里struct s是不完整类型。 struct s{ int i; float f; }; //声明结构标记s,这里struct s是完整类型。
同一作用域内同一对象或者函数的所有声明应为兼容类型。
int func(int, int *); int func(int, int[*]);