声明(十四)
七、声明符
C语言中声明符(declarator)具有以下语法格式:
pointeropt direct-declarator
其中pointer具有以下语法格式:
* attribute-specifier-sequenceopt type-qualifier-listopt * attribute-specifier-sequenceopt type-qualifier-listopt pointer
其中direct-declarator具有以下语法格式:
identifier attribute-specifier-sequenceopt ( declarator ) array-declarator attribute-specifier-sequenceopt function-declarator attribute-specifier-sequenceopt
其中attribute-specifier-sequence是属性说明符序列;type-qualifier-list是类型限定符列表;array-declarator是数组声明符;function-declarator是函数声明符。
在一个声明中每个声明符声明一个对象标识符、函数标识符或者类型标识符。
const int ci = 0; int func(void); struct s{ int i; double d; };
标识符前面的说明符表示标识符的类型、存储类别或者其它特性。
const int ci = 0; static int si = 1; alignas(8) int i8;
完整声明符(full declarator)是不属于其它声明符的声明符。
//i是完整声明符。 int i; //*ptr是完整声明符。 //ptr不是完整声明符。 int *ptr = NULL; //*arr[5]是完整声明符。 //arr和arr[5]不是完整声明符。 int *arr[5]; //*func(int)是完整声明符。 //func不是完整声明符。 int *func(int);
每个完整声明符后都存在一个序列点(sequence point)。
int *ptr = (int *)&ptr; //合法。
如果完整声明符的声明符嵌套序列中存在声明符指定了变长数组类型(variable length array type),那么该完整声明符指定的类型称为可变修改类型(variably modified type);此外通过声明符类型推导从可变修改类型派生出来的任何类型都是可变修改类型。
int n = 3; //(*ptr)[n]是完整声明符。 //[n]是嵌套声明符,具有变长数组类型。 //指针ptr的类型为int (*)[n],是可变修改类型。 int (*ptr)[n];
如果声明符具有以下语法格式:
identifier attribute-specifier-sequenceopt
可选的属性说明符序列适用于声明实体。
int i [[gnu::aligned(8)]]; //i的对齐值为8。
如果声明符具有以下语法格式:
( declarator )
括号内的声明符与没有括号的声明符完全相同;但复杂声明符的绑定可以通过括号来改变。
int (i); //等价于int i;。 int (*p); //等价于int *p;。 //等价于int *f(int *);。 //与int (*f)(int *);不同,一个是函数,另一个是指向函数的指针。 int (*f(int *));
实现可以直接或者通过typedef限制修改算术、结构、联合或者void类型的指针、数组和函数声明符的数量。ISO/IEC 9899:2024标准要求实现应至少支持12个指针、数组和函数声明符(任意组合)修改声明中的算术、结构、联合或者void类型。
1、指针声明符
如果指针声明符具有以下语法格式:
* attribute-specifier-sequenceopt type-qualifier-listopt direct-declarator
可选的属性说明符序列适用于声明的指针;与声明指针指向的对象无关。可选的类型限定符限定的是指针。
int i = 0; const int ci = 3; //指针ptc指向对象的值不能改变; //但指针ptc可以指向其它对象。 const int *ptc = &ci; //指针cp指向的位置不变,只能指向对象i,不能指向其它位置; //但指针cp指向对象的值可以改变。 int * const cp = &i; //属性[[gnu::aligned(16)]]适用于指针ptr,指定的是指针ptr的内存对齐, //与指针ptr指向的对象无关。 int * [[gnu::aligned(16)]] ptr = nullptr;
如果指针类型是兼容类型,两者应具有相同的类型限定符,并指向兼容类型。
int i = 0; //cp和vp类型不兼容,因为类型限定符不同。 int * const cp = &i; int * volatile vp = nullptr; //ptc和ptv类型不兼容,因为指向对象类型不兼容。 const int *ptc = nullptr; volatile int *ptv = nullptr;