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应该用在:
数组,结构,指针,函数的组合类型
可移植类型