小结
概念
■ struct是用来构造或定义一种新的数据类型的关键字(如同数组的“[]”或函数类型的“()”),用struct关键字定义的数据类型被统称为结构体(Structure)类型。
■ 和数组一样,结构体类型属于一种“聚合”数据类型(Aggregate Types),这种类型需要编程者自己根据实际问题自行构造。
■ 结构体类型可以拥有若干数据成员,这些数据成员的类型可以相同也可以不同。
■ 使用Structure类型数据通常要经过两个步骤:结构体类型的声明及定义这种类型的变量。
■ 除非代码中只有一种结构体类型,否则需要编程者为每种结构体类型命名一个特定标记(Tag)。
■ 结构体类型的名称由struct关键字和标记组成。
■ 结构体类型数据在内存中占据的空间大于或等于各个成员所占据的内存空间之和。
■ 结构体类型数据可以进行“.”、“=”等运算。
■ “.”是一种后缀运算符,其优先级为16,结合性为从左到右。
■ 结构体的成员变量可以作为左值参与运算。
■ 函数的形参或实参可以是结构体类型,函数的返回值也可以是结构体类型。
■ 结构体类型变量可以在定义时被赋初值,语法格式为“=”后跟一“{}”,在“{}”内填写各个成员变量的初值,各个初值之间用“,”分隔。在C99中允许按照成员名称(不一定按照成员次序)赋值。
■ C99中允许结构体类型的“常量”——“复合字面量”,除了常量不可以进行的运算,这种类型的数据能够进行任何结构体类型的变量可以参与的运算。
■ 可以在声明结构体类型的同时定义结构体类型的变量,但这是以牺牲代码的灵活性为代价的。
■ _Complex和_Imaginary是C99新增加的两个关键字,用于描述复数数据类型和虚数数据类型,但C语言对编译器是否应该支持_Imaginary类型并没有做强制要求。
■ _Complex和_Imaginary类型都属于算术类型。
■ union也是用来构造或定义一种新的数据类型的关键字,用union关键字定义的数据类型被统称为联合体(Union)类型。
■ 使用联合体类型同样需要经过声明数据类型和定义变量这样两个步骤。
■ 声明联合体类型的方法与声明结构体类型的方法基本一致,不同之处在于要使用union关键字。
■ 联合体类型数据的各个成员占据内存空间的起点相同。
■ 只能对联合体类型变量的第一个成员初始化。
■ 位运算可以方便地实现对数据的个别位进行操作。
■ 位运算的运算对象必须是整数类型。
■ C语言一共有6种位运算运算符:“~”、“&”、“^”“|”、“<<”、“>>”。
■ “~”运算的运算结果是运算与对象的各个位相反数的值。
■ “&”作为二元运算符时是按位与运算,其两个运算对象相应的位皆为1时运算结果对应的位上的值也为1,否则运算结果对应的位为0。
■ “^”表示按位异或运算,当两个操作数相应的位一个为0而另一个为1时,运算结果对应的位上的值为1,否则结果对应的位为0
■ “按位或”运算的运算规则是:相应的位皆为0时,运算结果对应的位上的值为0,否则结果对应的位为1。
■ 左移运算的结果是,将左操作数机器数的值向左移动右操作数所规定的位数所得到的结果。移出数据边界的各位被舍弃,缺少的各位补0。
■ 右移运算的结果是,将左操作数机器数的值向右移动右操作数所规定的位数所得到的结果。移出数据边界的各位被舍弃。最高位不表示符号时,移出的空位上补0;最高位为负符号时,移出的空位上补0或补1取决于编译器的规定。
■ 位段(Bit Fields)是指在结构体或联合体中被指定了拥有特定位数的成员,这些成员必须是整数类型。
■ 在结构体或联合体类型中定义位段成员的一般方法是:数据类型[成员名称]:数据宽度;。
■ 位段成员的数据宽度必须是一个非负的整数类型常量表达式,且最大值不可超过一定的限度。
■ 可以定义无名位段,这个位段在代码中无法被引用。
■ 位数为0的无名位段表示下一位段从另一个存储单元开始存放。
■ 位段成员不可以跨越“存储单元”的边界,这个“存储单元”通常是一个计算机字。
■ 位段成员的数据类型可以是“signed int”、“unsigned int”、“int”这几种类型,C99还允许_Bool类型。
■ “int”类型位段成员可能是“signed int”类型或“unsigned int”类型,与“char”类型是“signed char”类型或“unsigned char”类型一致,取决于编译器。
■ 位段无法作为左值参与“sizeof”、“&”等左值才能参与的运算,也不可以构造位段数组。
■ 位运算与位段操作都不具备可移植性,其结果与计算机采用的码制(原码、补码、反码)、数据的存储方向、数据的对齐规则等有关。
风格
结构体或联合体数据类型的声明通常应该在源代码的最前面而不应该放在某个函数内部。这是因为结构体或联合体数据类型通常是全局性的。
把结构体或联合体数据类型的声明和结构体或联合体类型变量放在一起通常不是很常规的做法,尽管这并不违背C语言的语法。应该养成把结构体或联合体类型声明与变量定义分开进行的习惯。
可以利用#define预处理命令使结构体或联合体类型的名称更为简洁,但这并不是很正式的一种风格。
常见错误
■ 在声明结构体类型时忘记“}”之前或之后的“;”。
■ 用不同的结构体类型变量赋值。
■ 混淆逻辑运算符“&&”与按位或运算符“&”。
■ 混淆逻辑运算符“||”与按位或运算符“|”。
■ 误认为“^”是乘方运算。
■ 在写表达式时忽视了“&”、“|”、“^”等位运算的优先级。
■ 定义位段数组。
■ 进行“<<”、“>>”运算时右操作数为负数或超过左操作数的位数。
■ 错误地用非第一个成员类型的值对联合体变量进行初始化。
■ 试图以传递结构体或联合体实参的方式,通过函数改变调用处结构体或联合体变量的值。实际上,在被调用函数中无法通过改变结构体或联合体的值改变调用函数处定义的结构体或联合体变量的值。
忠告
在函数间传递尺寸很大的结构体类型数据,通常非常影响程序的执行效率。
牛角尖
这种类型声明可以吗?
答:可以。因为尽管a是结构体类型名称的标记,但这个标记从来不会单独使用,只会与struct关键字一同使用,所以编译器能够区别开作为结构体类型名称的标记a与成员名称a。但是必须要明白的是,这是一种很烂的编程风格。