设为首页 加入收藏

TOP

C语言结构、联合、位操作、位域和枚举(二)
2014-11-23 21:37:36 来源: 作者: 【 】 浏览:10
Tags:语言 结构 联合 操作 枚举

\
这是一个关于发牌的例子,还是比较好的。首先定义纸牌结构的时候,成员变量的类型是const char *,这样,便不能通过对该指针进行解参考来修改指针指向的变量的值,却可以修改指针本身的指向,很明显这样是安全(符合一般程序设计中的最低权限原则)而且合理的。void fillDeck(Card * const wDeck,const char *wFace[],const char *wSuit[]);、void shuffle(Card *const wDeck);和void deal(const Card * const wDeck);等函数中const的运用十分巧妙的控制了函数对参数的访问权限,十分漂亮。

2、联合(union)< http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+waq6z8rH0rvW1sXJyfq1xMr9vt3A4NDNo6zL/NPrveG5ucDgJiMyMDI4NDujrMf4sfDU2tPawaq6z7XEs8nUsbmyz+3P4M2stcS05rSiv9W85KGj1NqzzNDy1tDT0NXi0fm1xMfpv/ajrMSz0Kmx5MG/1q685MrHz+C52LXEoaPV4tH5vs2/ydLUyrnTw8Gqus/AtLbU1eLQqbHkwb+1xMT0tOa/1bzkvfjQ0Lmyz+2jrMC0sdzD4rK7sdjSqrXExNq05r/VvOTAy7fRoaPBqrrPtcSzydSxv8nS1MrHyM7S4sDg0M2jrLTmtKLBqrrPy/nQ6NKqtcTX1r3ayv2x2NDr1sHJ2cTcubu05rSiwaq6z9bQ1by/1bzk1+6087XEs8nUsaGjtuDK/cfpv/bPwqOswaq6z7D8uqzBvdbWu/LBvdbW0tTJz7XEyv2+3cDg0M2jrMO/tM7Wu8Tc0v3Tw9K7uPazydSxo6zSsr7NysfWu8Tc0v3Tw9K71tbK/b7dwODQzaGjyrnTw8qxo6zTprjD16LS4tPD1f3It7XEwODQzcC00v3Tw8Gqus/W0LXEyv2+3aGjPC9wPgo8cD7U2sGqus/Jz7/J0tTWtNDQtcSy2df309Cjur2rwaq6z7izJiMyMDU0MDu4+M/gzazA4NDNtcTB7dK7uPbBqrrPoaK78cihwaq6z7XEtdjWtygmYW1wOynS1LywyrnTw73hubmzydSx1MvL47f7us294bm51rjV69TLy+O3+8C0t8POysGqus+zydSxoaPA4CYjMjAyODQ7tdijrNKysrvE3NPDPT26zSE91MvL47f7wLTAtLHIvc/BqrrPoaM8L3A+CjxwPjIuMSC2qNLlus2z9cq8u688L3A+CjxwPsGqus+1xLao0uW6zb3hubnA4CYjMjAyODQ7o6zPwsPmysfSu7j2wP3X06O6PC9wPgo8cD48cHJlIGNsYXNzPQ=="brush:java;">union number { int x; double y; };和struct一样,上面仅仅是创建了一个类型,还没有用该类型来定义变量。在联合变量的定义中,仅能用于第1个联合类型相同的值来初始化联合,因为上面的例子中联合成员的第一个类型是int,所以语句union number value={10};正确地初始化了变量value。但语句union number value={3.1415}将截断浮点数值的小数部分,并通常会产生编译警告。

2.2 联合的例子

#include 
  
   
union number
{
	char c;
	unsigned int i;
};
int main()
{
	union number value;

	value.c='A';
	printf("%s:\nchar:%c\nunsigned int:%d\n","Put a value int the char member",value.c,value.i);

	value.i=97;
	printf("%s:\nchar:%c\nunsigned int:%d\n","Put a value int the unsigned int member",value.c,value.i);

	value.c='A';
	printf("%s:\nchar:%c\nunsigned int:%d\n","Put a value int the char member",value.c,value.i);
	return 0;
}
  
运行结果:

\
需要说明的是联合这种数据结构可能不能轻易地移植到其它计算机系统上。联合能否被移植通常依赖于给定系统上存储联合成员数据类型时所使用的对齐方式。

2.3 联合的使用

从上面看,仿佛联合这个东西是一个怪物,谁会自找麻烦去用这个啊。我也是这么想的,结果在网上找到一篇文章专门写联合的使用的。摘了一写东西,贴在这里。

2.3.1 增加代码的可读性

struct  Matrix
{
    union
    {
        struct
        {
            float  _f11, _f12, _f21, _f22;
        };
        float  f[2][2];
    }_matrix;
};
struct  Matrix m;
该例子中,struct和float f[][]共享内存空间,没有造成内存空间的浪费。这样,用矩阵的时候可以用m._matrix.f(比如说传参,或者是整体赋值等);需要用其中的几个元素的时候可以用m._matrix._f11,可以避免用m.f[0][0](不直观,且容易出错)。

2.3.2 union和强制类型转换

这里需要说明的是union里面的成员都是从低地址开始对齐的。拿上面定义的number联合作为例子,其中的char成员c和unsigned int成员i,它们在内存中的分布应该如下图:

\
下面是一个利用判断大小端的例子,分别采用union和非union的实现:

#define TRUE 1
#define FALSE 0
#define BOOL int
//不用union
BOOL  isBigEndian()
{
    unsigned int  i = 1;   /* i = 0x00000001*/
    char  c = *(char  *)&i; /* 注意不能写成 char c = (char)i; */
    return  (int )c != i;
}
//用union
BOOL  isBigEndian()
{
    union
    {
        unsigned int  i;
        char  c;
    }test;
    test.i = 2;
    return  test.c != 2;
}
3、位运算

在计算机内部是使用位序列来表示所有数据的。每一位的值可以是0或者是1。因此,利用位运算比较方便,而且通常比较高效。

3.1 C语言提供了的位运算符

&|^ 对两个操作数按位进行与、或、异或操作
<<左移 将第一个操作数的各位向左移动第二个操作数所指定的位数;在右边用0来填充空位,向左移动到边界之外的1将丢失。
>>右移 将第一个操作数的各位向右移动第二个操作数所指定的位数;填充左边的方法依赖于计算机。对于unsigned整数执行右移将使得左边的空位用0代替,移动到右边界之外的1将丢失。如果右边的操作数是负值,或者右边的操作数大于存储左边操作数的位数,则移位的结果是不确定的。
~取反 将操作数按位取反
3.2 一个例子:以二进制的形式输出无符号整数

#include 
  
   
void displayBits(unsigned value);
int main()
{
	unsigned x;
	printf("Enter an unsigned integer:\n");
	scanf("%u",&x);
	displayBits(x);
	return 0;
}
void displayBits(unsigned value)
{
	unsigned c;
	unsigned displayMask=1<<31;
	printf("%10u= ",value);
	for (c=1;c<=32;c++)
	{
		putchar(value&displayMask '1':'0');
		value<<=1;//左移一位
		if(c%4==0)
		{
			putchar(' ');
			if(c%8==0)
				putchar(' ');
		}
	}
	putchar('\n');
}
  
运行结果:

\
4、位域

C语言中,可以指定结构或者联合中unsigned或者int成员的位数,这些位称为位域。位域在所需要的最小位数内存存储数据,因此,可以更好地利用内存。位域成员必须声明为int或者unsigned。

4.1 位域的声明

struct bitCard
{
	unsigned face : 4;
	unsigned suit : 2;
	unsigned color : 1;
};
该定义包含3个unsigned位域,即face、suit和color,用于表示52张纸牌中的一张纸牌。在unsigned或者int成员名称的后面加入冒号和表示位宽度的整数常量(也就是存储成员所需的位数),就可以声明位域。表示宽度的常量必须是0和 系统上存储int的总位数之间的一个整数。

struct example
{
	unsigned a : 13;
	unsigned   : 19;
	unsigned b : 4;
};
如上面的例子,我们也可以指定没有命名的位域,其中的字段可以用来做填充内容。上面例子中没有命名的19位作为填充内容,在这19位中不能存储任何东西,只是用来保证成员b存储在另一个存储单元(这里讨论的是4字节的机器)。

struct example
{
	unsigned a : 13;
	unsigned   : 0;
	unsigned b : 4;
};
宽度为0的无名位域可以用于在新的存储单元边界上对齐下一个位域。上面的例子中使用没有命名且宽度为0的位域来跳过存储a的存储单元中的剩余位(无论有多少),这使得b和下一个存储单元边界对齐。

4.2 使用位域的好处和缺点

因为位域没有地址,所以不能用&来获得位域的地址,另外,也不能像数组元素那样访问位域内的单个位。尽管位域可以节省空间,但是它们会使编译器产生执行速度较慢的机器语言代码。因为机器语言代码与需要额外的步骤来访问可寻址存储单元中的部分。这是一种典型的时间换空间的实现。

4.3 例子

#include
  
   
struct bitCard 
{
	unsigned face : 4;
	unsigned suit : 2;
	unsigned color: 1;//1 bit
};
typedef struct bitCard Card;
void fillDeck(Card *const wDeck);
void deal(const Card * const wDeck);

int main()
{
	Card deck[52];
	fillDeck(deck);
	deal(deck);
	return 0;
}
void fillDeck(Card *const wDeck)
{
	int i;
	for (i=0;i<=51;i++)
	{
		wDeck[i].face=i%13;
		wDeck[i].suit=i/13;
		wDeck[i].color=i/16;
	}
}
void deal(const Card * const wDeck)
{
	int i;
	for (i=0;i<=51;i++)
		printf("Card:%2d Suit:%2d Color:%2d%s",wDeck[i].face,wDeck[i].suit,wDeck[i].color,(i+1)%3 " | ":"\n");
	printf("\n");
}
  
运行结果:

\
5、枚举

C语言提供的最后一个用户自定义类型称为枚举。由关键字enum声明的枚举是用标识符表示的一组整数常量。实际上,这些枚举常量是可以自动设置值的符号常量。枚举中的值从0开始,每次增加1(除非特别指定)。

5.1 枚举声明

如声明enum months{JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};创建了一个新类型enum months,其中标识符设置为整数0到11。如果要记录月份1-12,可以采用enum months{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};,第一个值被显示地设置为1,后续的其余值从1开始累加,从而产生1到12的值。枚举中的标识符必须是唯一的。通过标识符赋值,可以明确地在定义中设置枚举的每个常量值。枚举的多个成员可以具有相同的常量值。

5.2 例子

#include 
  
   
//声明一个枚举类型
enum months {JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};
int main()
{
	//定义一个枚举变量
	enum months month;
	const char *monthName[]={"*Error*","January","February","March","April",
		"May","June","July","August","September","October","November","December"};
	for (month=JAN;month<=DEC;month=(enum months)(1+(int)month))
	{
		printf("%2d %10s%c",month,monthName[month],month%2 '\t':'\n');
	}
	return 0;
}
  
运行结果:

\
好了,又over了一篇。尽管充其量只能算是笔记,我还是像耐着性子把它们写完,就算是锻炼心态了。微笑

参考:http://blog.csdn.net/jiangnanyouzi/article/details/3158702和

首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇GTK编程基础----按钮构件 下一篇Objective-c官方文档 封装数据属性

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: