C和指针---第十章:结构和联合

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;
	};
};


转载于:https://my.oschina.net/voler/blog/162636

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值