C语言结构、联合、位操作、位域和枚举
1、结构
结构是派生的数据类型,可以使用其他数据类型来构造它们。
1.1 定义结构和结构类型的变量
关键字struct引入了结构定义,用一个标识符作为结构标记,来命名一个结构类型。结构定义大括号内声明的变量是结构的成员。同一结构的成员必须具有具有独一无二的名称,但两个不同的结构可能包含相同名称的结构成员,而不会相互冲突。每个结构定义必须用分号结束。
结构标记与关键字struct一起用来定义结构类型的变量,也可以通过在结构定义的右大括号和结束结构定义的分号之间加入逗号分隔的变量名列表来定义结构变量。关键字typedef也提供了为前面定义的数据类型创建别名(或者同义词)的机制。因此,也可以用typedef为结构类型建立比较短的类型名称。typedef不会创建新类型只会为已经创建的类型定义了新的名称,可以用作已创建类型的别名。
结构不能包含本身的实例,但是可以包含指向结构自身的指针,这里称为自引用结构。也就是说,在定义结构A的时候,不能有A类型的结构成员变量,但是可以有struct A *类型的成员变量(也就是说可以包含指向A类型的指针变量,也就是成员变量引用A自身,这很好理解吧)。需要注意的是结构的定义并没有在内存中分配任何空间,而只是创建了用户需要的用于定义变量的新的类型。定义结构类型变量的时候才会导致内存空间的分配。可以在结构上执行的操作有:将结构变量赋给相同类型的结构变量,获得结构变量的地址(&),访问结构变量的成员和使用sizeof运算符来确定结构变量的大小。
下面是几种关于结构和结构变量的定义是等价的:
(1)struct card{ char *face; char *suit;};struct card aCard,deck[52],*cardPtr;(2)struct card{ char *face; char *suit;}aCard,deck[52],*cardPtr;(3)struct{ char *face; char *suit;}aCard,deck[52],*cardPtr;(4)typedef struct{ char *face; char *suit;}Card;Card aCard,deck[52],*cardPtr;
可以看出结构标记是可选的,如果没有结构标记,那么就只能在结构定义的时候定义结构类型的变量。为了养成良好的编程习惯和风格,定义结构时提供结构标记还是很有必要的,能够方便代码中稍后定义的结构类型的变量。
1.2 结构的内存对齐问题
由于结构成员不一定是存储在内存中的连续字节中,所以不能用==和!=来比较结构。有时候,因为计算机可能仅在某些内存边界上存储特定的数据类型,如半个字、字或者双字(字是标准内存单元,它用于在计算机中存储数据,通常是两个字节或者四个字节)边界,所以在存储结构的内存区域中可能会有“洞”。这就是传说中的内存对齐问题。
struct E { char c; int i;}e1,e2;上面的例子中,使用两个字节作为字的计算机可能需要在字边界上对齐struct E的每个成员,也就是在字的开头处(这是和机器相关的)进行对齐。下图中,说明了结构体成员变量在内存中的对齐情况,其中变量已经被赋值为字符‘a’和整数97。
如果成员存储在字边界的开头处,则在类型struct E变量的存储空间中有一个字节的空洞,如上图中所示。空洞中的值是没有定义的。如果e1和e2成员变量的值实际上相等,但可能因为在空洞中包含不同的值,所以结构比较并不一定相等。
1.3 结构的初始化
可以像数组那样使用初始值列表来初始化结构。要初始化结构,需要在定义的结构变量名后面加入的等号,以及使用逗号分隔的初始化列表,并在外面加大括号。例如语句struct card aCard={"Three","Hearts"};在定义变量aCard是struct card类型的同时也将其成员face初始化为“Three”,suit初始化为"Hearts"。如果列表中初始值的个数小于结构成员的个数,则将把剩余的成员自动初始化为0,如果成员变量是指针,则初始化为NULL。如果在函数外部定义的结构变量没有进行显示地初始化,那么其结构变量将初始化为0或者NULL。我们也可以在赋值语句中初始化结构变量:相同类型的结构变量之间赋值,或者对结构的单个成员进行赋值。
1.4 访问结构成员
有两个运算符可以用于访问结构成员:结构成员运算符(.)和结构指针运算符(->)。结构成员运算符通过结构变量名来访问结构成员,如printf("%s/n",aCard.suit);便可以访问到aCard结构变量的成员suit;结构指针运算符通过结构指针来访问结构成员,它由负号和大于号构成,两个符号之间没有空格,假设指针cardPtr是指向struct card的指针,那么语句printf("%s/n",cardPtr->suit);便可以访问到cardPtr所指向对象的suit成员。cardPtr->suit等价于(*cardPtr).suit。(由于结构成员运算符比解参考运算符的优先级要高,所以要加括号。)
1.5 在函数中使用结构
可以把单个结构成员、整个结构或者结构指针传递给函数。当把结构或者单个结构成员传递给函数时,它们采用的是值调用传递。所以,被调函数不能修改主调函数中结构的成员。要使用引用调用来传递结构,则需要传递结构变量的地址。结构数组和所有其它数组一样,都是自动使用引用传递。应该知道的是,使用引用调用来传递结构要比使用值调用来传递结构效率要高,因为引用调用不用复制整个结构。
1.6 一个结构的例子
#include运行结果:#include #include struct card{ const char *face; const char *suit;};typedef struct card Card;void fillDeck(Card * const wDeck,const char *wFace[],const char *wSuit[]);void shuffle(Card *const wDeck);void deal(const Card * const wDeck);int main(){ //定义数组存储纸牌 Card deck[52]; //牌面数组 const char *face[]={"Ace","Deuce","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"}; //花色 const char *suit[]={"Hearts","Diamonds","Clubs","Spades"}; //设置随机数发生器的种子 srand(time(NULL)); //load the card to deck fillDeck(deck,face,suit); //shuffle the card shuffle(deck); //distribute deal(deck); return 0;}void fillDeck(Card * const wDeck,const char *wFace[],const char *wSuit[]){ int i; for (i=0;i<=51;i++) { wDeck[i].face=wFace[i%13]; wDeck[i].suit=wSuit[i%4]; }}void shuffle(Card *const wDeck){ int i; int j; Card temp; //随机选出一张放在第i个位置 for (i=0;i<=51;i++) { j=rand()%52; temp=wDeck[i]; wDeck[i]=wDeck[j]; wDeck[j]=temp; }}void deal(const Card *const wDeck){ int i; for (i=0;i<=51;i++) printf("%5s of %-8s%c",wDeck[i].face,wDeck[i].suit,(i+1)%3?'/t':'/n');}
这是一个关于发牌的例子,还是比较好的。首先定义纸牌结构的时候,成员变量的类型是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)<喎
- 09-29如何通过wrap malloc定位C/C++程序的内存泄漏
- 02-25打车软件大战升级,补贴还能维持多久?
- 12-23BMP文件右旋90度[c语言]
- 12-23寻找直方图中面积最大的矩形(C语言版)
- 12-23[ndk,2]ndk开发案例和错误处理
- 12-23[ndk,1]ndk开发,C语言入门讲解
- 12-23C语言连续存储实现队列机制
- 12-23Objective-c 数据类型
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 12-05亚马逊推出新一代基础模型 任意模态生成大模
- 12-05OpenAI拓展欧洲业务 将在苏黎世设立办公室
- 12-05微软质疑美国联邦贸易委员会泄露信息 督促其
- 12-05联交所取消宝宝树上市地位 宝宝树:不会对公
- 12-04企业微信致歉:文档打开异常已完成修复