10.1 结构基础知识
聚合数据类型能够同时存储超过一个的单独数据。C语言提供了数组和结构两种聚合数据类型。
数组是相同类型的元素的集合,它的每一个元素是通过下标引用或指针简介访问来选择的。而结构也是一些值的集合,这些值称为它的成员,但一个结构的各个成员可能具有不同的类型。
数组元素可以通过下标访问,因为数组的元素长度相同。但是由于结构的成员可能长度不同,所以不能使用下标来访问它们。相反,每个结构成员都有自己的名字,它们是通过名字访问的。
这个区别非常重要。结构并不是一个它自身成员的数组。当一个结构变量在表达式中使用时,它并不被替换成一个指针。结构变量也无法使用下标来选择特定的成员。
10.1.1 结构声明
在声明结构时,必须列出它包含的所有成员。这个列表包括每个成员的类型和名字:
struct tag { member-list } variable-list;
例如:
struct {
int a;
char b;
float c;
} x;
这个声明创建了一个名叫x的变量,它包含三个成员:一个整数,一个字符和一个浮点数。
struct {
int a;
char b;
float c;
} y[20], *z;
这个声明创建了y和z。y是一个数组,它包含了20个结构。z是一个贺子珍,它指向这个类型的结构。
警告:这两个声明是截然不同的两种类型,即使它们的成员列表完全相同。因此,变量y,z的类型和x的类型不同。所以下面这条语句:
z = &x;
是非法的。
但是,标签可以解决这个问题。
标签(tag)字段允许为成员列表提供一个名字,这样它就可以在后续的声明中使用。标签允许多个声明使用同一个成员列表,并且创建同一种类型的结构。例如:
struct SIMPLE {
int a;
char b;
float c;
};
我们可以声明相同类型的x,y,z了。
struct SIMPLE x;
struct SIMPLE y[20], *z;
备注:SIMPLE只是一个标签,所以声明前必须使用struct。
我们也可以通过typedef来声明一种新的类型:
typedef struct {
int a;
char b;
float c;
} Simple;
备注:Simple是新类型,所以声明时候不需要struct:
Simple x;
Simple y[20], *z;
备注:如果你想在多个源文件中使用同一种类型的结构,你应该把标签声明或typedef形式的声明放在一个头文件中。当源文件需要这个声明时可以使用#include指令把那个头文件包含进去。
10.1.2 结构成员
备注:结构只是一种类型!
struct COMPLEX{
float f;
int a[20];
long *lp;
struct SIMPLE a;
struct SIMPLE sa[10];
struct SIMPLE *sp;
};
10.1.3 结构成员的直接访问
结构变量的成员通过点操作符(.)访问的。
10.1.4 结构成员的间接访问
通过箭头操作数(->)来访问。
10.1.5 结构的自引用
在一个结构内部包含一个类型为该结构本身的成员是否合法呢?例如:
struct SELF_REF1{
int a;
struct SELF_REF1 b;
int c;
};
这种类型的自引用是非法的。因为b是一个完整的结构,会不断的自身引用,导致无穷的递归。
但是下面的声明确实合法的:
struct SELF_REF1{
int a;
struct SELF_REF2 *b;
int c;
};
因为b是一个指针。编译器在结构的长度确定之前就已经知道指针的长度了,所以这种类型的自引用是合法的。指针实际上指向的是同一种类型的不同结构!!!注意是不同的结构。
警告:
警惕下面的陷阱:
typedef struct {
int a;
struct SELF_REF3 *b;
int c;
} SELF_REF3;
因为类型名直到声明的末尾才定义,所以在结构声明中的内部它尚未定义。
解决方案是定义一个结构标签来声明b:
typedef struct SELF_REF3_TAG {
int a;
struct SELF_REF3_TAG *b;
int c;
} SELF_REF3;
10.1.6 不完整的声明
如果两个结构体相互引用,那么哪个先声明?
答案:通过不完整声明来解决:
struct B;
struct A {
struct B *partner;
};
struct B {
struct A *partner;
};
10.1.7 结构的初始化
和数组很相像。一个位于一对花括号内部,由逗号分隔的初始值列表可用于结构各个成员的初始化。这些值根据结构成员列表的顺序写出。如果初始列表的值不够,剩余的结构成员将使用缺省值进行初始化。
struct INTF_EX {
int a;
short b[10];
Simple c;
}x = {
10,
{
1,2,3,4,5
},
{
25, 'x', 1.9
}
};
10.2 结构,指针和成员
typedef struct {
int a;
short b[2];
} Ex2;
typedef struct Ex{
int a;
char b[3];
Ex2 c;
struct Ex *d;
} Ex;
Ex x = {10, "Hi", { 5, { -1, 25 }, }, 0 };
Ex *px = &x;
10.2.2 访问结构
表达式*px + 1是非法的,因为*px是一个结构,而C语言并未定义结构和整型值的加法。
而表达式*( px + 1 )中,1实际上代表的是一个结构体的移位,但是我们并为定义下一个结构体,所以这个表达式也是非法的。
10.2.3 访问结构成员
比较下面两个表达式:
*px和px->a。它们有什么区别?
1. 它们具有相同的地址,所以*px == px->a。
2. 但是,它们类型是不同的!!!变量px被声明为一个指向结构的指针,所以表达式*px的结果是整个结构,而不是它的第一个成员。
我们创建一个指向整型的指针:
int *pi;
表达式pi = px;是非法的,因为pi和px的类型不同,即使进行了强制转换也是非法的:pi = (int *)px;
正确的写法应该如下:
pi = &px->a;
10.2.4 访问嵌套的结构
我们来分析一个表达式:
*px->c.b;
比较好理解。唯一要正确认识到的是:px->c.b是一个数组名,而数组名是指针常量,所以可以直接用*来读取。这里的值是数组b的第一个元素。
10.2.5 访问指针成员
px->d;
其结果是0。但是
*px->d;
则是一个指向结构的指针。
Ex y;
x.d = &y;
px->d->c.b[1];
这里要注意一点是:px->d指向是的一个Ex的指针。
10.4 作为函数参数的结构
假设有一个结构体:
typedef struct {
char product[PRODUCT_SIZE];
int quantity;
float unit_price;
float total_amount;
} Transaction;
我们在一个函数中调用了这个结构体;
void print_receipt( Transcation trans ){
printf("%s\n", trans.product);
printf("%d @ %.2f total %.2f\n", trans.quantity, trans.unit_price, trans.total_amount );
}
这里存在极度的资源浪费,我们需要拷贝整个Transcation进去。
实际上可以用指针的:
void print_receipt( Transcation *trans ){
printf("%s\n", trans->product);
printf("%d @ %.2f total %.2f\n", trans->quantity, trans->unit_price, trans->total_amount );
}
用指针有个显而易见的好处是:资源并不存在浪费的现象。
其次我们可以直接对原数组进行修改。
10.6 联合
联合的所有成员引用的是内存中的相同位置。当你想在不同的时刻把不同的东西存储于同一个位置时,就可以使用联合。
#include <stdio.h>
int main(void)
{
union {
float f;
int i;
} fi;
fi.f = 3.14159;
printf("%d\n", fi.i );
printf("%d\n", (int)fi.f );
return 0;
}
程序输出:
第二个输出为3倒没什么疑问,第一个为什么不也是3呢?
因为float类型在内存中的存储方式决定的,即3.14159的二进制码转成十进制后就是1078530000.
10.6.2 联合的初始化
联合变量可以被初始化,但是这个初始值必须是联合第一个成员的类型,而且它必须位于一对花括号里面。
union {
int a;
float b;
char c[4];
} x = { 5 };
把x.a初始化为5.
我们不能把这个类初始化为一个浮点值或者字符值。如果给出的是任何其他的类型,它将自动转换为一个整数并赋值给x.a。
习题:
1.
#define MAX_SIZE 128
typedef struct {
long num1;
long num2;
long num3;
}Num;
struct inf{
char date[MAX_SIZE];
char time[MAX_SIZE];
struct telephoneNumber
{
Num yourNum;
Num otherNum;
Num payNum;
};
};
2.
typedef struct Money{
float msrp;
float asp;
float st;
float lf;
}Money;
typedef struct Sales{
float msrp;
float asp;
float dp;
float sd;
float mp;
int lt;
}Sales;
typedef struct RunSales{
float msrp;
float asp;
float st;
float lf;
float dp;
int ld;
float ir;
float mp;
char nob[20];
};
struct inf{
char name[20];
char addr[20];
union model{
Money moneyModel;
Sales salesModel;
RunSales runsalesModel;
};
};