C语言的声明分析

时间:2023-01-23 22:22:55

ANSIC的typedef说明符之所以被称为“存储类型说明符”只是为了语法上的方便而已。

1.关于结构

结构的通常形式:

struct 结构标签(可选){

类型1 标示符1;

......

}变量定义(可选);

如果不选择这些可选的内容,则结构体的定义实际上是这样的:

struct

{

。。。。

};

你在后续声明这些结构体类型的变量是,必须这样:

struct

{

。。。。

} temp;

因此,结构标签的作用就是用:

struct 结构体标签

这种方式代替

struct

{

。。。。

};

即声明变量简化为:

struct 结构体标签 temp;

结构中允许存在位段,无名字段以及对齐所需要的填充字段。这些都是通过在字段的声明后面加上:和表示位段长度的整形数来实现的。这种用法通常称为深入逻辑元件的编程。位段的类型,必须是int unsigned int / signed int (或者加上限定符),至于int的值可不可以取负值,取决于编译器。

在使用位段时,要注意:

typedef struct _A
{
 unsigned a:4;//位段成员的类型仅能够为unsigned或者int
 signed   b:4;
 unsigned c:2;
 unsigned d:6;
 unsigned E:1;
 unsigned D:2;
 unsigned T:3;
 unsigned A:9;
 unsigned h:4; //前面已经为31,故4+31>32已超过一个存储单元,所以4在一个新的存储单元存放
 unsigned y:29;//由于前面的4在一个新的存储单元的开头存放,且29+4>32, 故在另一个新的存储单元存放
}A;               //所以最后求出的A的大小是4 + 4 + 4 =12

/*对上面的具体解释: 一个位段必须存储在同一个存储单元中,不能跨两个单元.如果某存储单元空间中不能容纳
                                     下一个位段,则改空间不用,而从下一个存储单元起存放该位段. 结构体A中的h和y就是这种情况.
                                         在gcc环境下,测试后,一个存储单元为4个字节.
 */

typedef struct _S
{
 unsigned a:4;
 unsigned b:4;
 unsigned c:22;
 unsigned q:1;
 unsigned h:1;
//    unsigned long i:33;  // 错误:‘i’ 的宽度超过它自身的类型
    unsigned i:1;//当多出此行时,该结构体大小由4变为8,因为此行之前正好为32位
} S;
typedef struct _T
 {       //当没有占满一个存储单元时,结构体的大小对齐为一个存储单元的大小
         unsigned a:2;
         unsigned b:2;
         unsigned j:1;
         unsigned : 1;//可以定义无名位段,此例中该无名位段占用1位的空间,该空间将不被使用
}T;
 
 typedef struct _V
 {
         unsigned a:1;
         unsigned b:4;
         unsigned :0;    //定义长度为0的位段时不能指定名字,否则编译不过
         unsigned d:1;   //定义了0字段后,紧接着的下一个成员从下一个存储单元开始存放;
 }V;                                     //此例子中,d前面那个存储单元中的余下的27位中被0填充了

 

建议,在声明变量时,将结构体声明和变量声明分开写,方便阅读。

另外,int类型的变量i的参数传递与只含有int类型的结构变量s的参数传递可能完全不同,int参数传递会传递到寄存器,为了速度,而结构参数则可能被传递到堆栈中。参数在传递时首先尽可能地放到寄存器中(追求速度)。

我们可以在结构体中只安排一个数组,则,该数组可以当做第一等级的类型,用于赋值语句拷贝整个数组,以值传递的方式传递到函数,或者作为函数的返回类型。

2.关于联合

联合的使用与结构一样,但在存储上,联合的每个成员都是从偏移零地址开始存储,这样某一时刻,只有一个成员真正存储在该地址。

联合一般是用在大型结构的一部分存在的,用来节省空间。

联合还可以用来将同一个数据解释成不同的东西,例如:

union bits32_tag{

int whole; //一个32位的整数

struct {char c0, c1, c2, c3;}byte;//4个8位的字节

} value;

当然,采用其他的方法也可以达到这个效果,但是联合不用额外赋值,或者强制转换。

3.关于枚举

实际上,C语言作为弱类型语言,很少有什么事只能靠枚举完成,而用#define不能解决,但枚举有个优点就是:可以在调试的时候监视数据,其名字一直可见,但#define则在编译时丢弃了名字。

4.关于优先级规则

这里有个例子:

char * const *(*next)();

利用优先级规则,我们进行如下分析:

1>.声明从它的名字开始,然后按优先级顺序依次读取,例子中,名字为next。

2>.优先级从高到底依次是:

    2.1.声明中被括号括起来的部分。则得出next是指向一个。。。的指针

    2.2.后缀操作符:

          ()表示一个函数,[]表示一个数组。则得出next是一个函数指针,指向一个返回。。的函数

    2.3.前缀操作符:

          *表示指向。。。的指针。则得出指针所指的内容。

3>.如果用const,volatile关键字的后面紧跟类型说明符,则它作用于类型说明符,其他情况下,它作用于坐标临近的指针*号。则把char *const 解释为指向字符的常量指针。

因此,该声明表示:next是一个指针,它指向一个函数,该函数返回一个指向字符型的常指针的指针。

5.关于typedef

普通的申明表示“这个名字是一个指定类型的变量”,而typedef关键字并不创建一个变量,而是宣称“这个名字是指定类型的同义词”。

不要在一个typedef中放入多个声明器,也不能把typedef嵌入到声明中间。尽管在语法上,这样做是可以的。

如:typedef int *ptr, (fun)(), arr[5];

unsigned const long typedef int volatile *kumquat;

一般情况下,typedef用来简洁的表示指向其他东西的指针。

typedef实际上是给一种类型引入一个新名字,类似于宏替换。但二者还是有区别的:

5.1 #define可以用其他类型说明符扩展,但typedfe却不可以。

例如:

#define peach int

unsigned peach i;这样可以

typedef int banana

unsigned banana i;这样却不可以

5.2连续几个变量的声明中,typedef可以对所有变量声明为同一类型,而#define不能。

 因此,关于typedef有以下注意:

不要为了方便对结构体使用typedef,应该始终在结构体的定义中,使用结构标签,即使它非必须。

typedef应该用在:

数组,结构,指针,函数的组合类型

可移植类型