C结构体

2/10/2017来源:ASP.NET技巧人气:940

结构体的定义

struct用于引出一个结构定义,其后的标识符被称为结构体标记(structure tag)。声明如下:

struct Node { int a; struct Node *next; };

结构体的类型是struct Node(类型名,如int等)。在结构体定义的花括号内声明的变量,被称为结构体的成员。同一个结构体成员必须具有不同的名字,但是两个不同的结构体类型中的成员可以有相同的名字而不发生冲突。每一个结构体必须要以分号结束。 结构体定义并不占用内存中的其他空间,只是创建了一种新的可用来定义变量的数据类型(struct+tag)。定义结构体变量如下:

struct Node node, nodes[50], *nodePtr;

分别定义了一个struct Node类型的变量,一个包含了50个struct Node类型的元素的数组,和一个指向该结构体类型的指针。也可通过如下方式定义变量:

struct Node { int a; struct Node *next; }node, nodes[50], *nodePtr;

结构体定义时可以省略标记名,但是如果省略了标记名,则该结构体类型变量的声明就只能和结构体定义同时进行,不能进行单独的声明。如下:

struct { int a; float b; }node, nodes[50], *nodePtr;

一个结构体不能包含它自身类型的实例,但是指向自身类型的指针可以出现。一个结构体定义中出现指向自身结构体体类型的指针成员的结构体,被称为自引用结构体。 可用于结构体的操作:将结构体变量赋值给其他具有相同类型的结构体变量,用&取得结构体变量的地址,访问一个结构体变量中的成员,sizeof确定结构体的大小。

结构体的初始化

总共有三种方式可以实现结构体初始化。 一、可以通过将一个结构体变量赋值给另一个与其类型相同的结构体变量,从而对后者初始化。 二、可以通过对结构体变量中的每一个成员分别进行赋值来实现初始化。 三、可以与数组一样采用初始值列表来初始化结构体,如果列表中的初始值个数少于结构体中成员个数时,则剩余没有初始值与之对应的成员,将被自动的初始化为0(当成员是指针时,被初始化为NULL)。对于函数之外定义的(即外部的)结构体变量,如果在定义时没有被显式的初始化,则自动被初始化为0或NULL。

结构体成员的访问

对结构体的访问可以通过两种运算符来实现,一个是结构体成员运算符(structure member Operator)(.),一个是结构体指针运算符(structure pointer operator)(->),也成为箭头运算符。 一、结构体成员运算符(.)通过结构体变量名来访问结构体成员。如:

struct Node node = {1, NULL}; PRintf("%d", node.a);

二、结构体指针运算符(一个减号和一个大于号组成,中间没有空格)通过指向结构体的指针来访问结构体成员。如:

struct Node node = {1, NULL}; struct Node * nodePtr = &node; printf("%d", nodePtr->a);

其中表达式nodePtr->a等价于*(nodePtr).a,圆括号不能少,因为结构体成员运算符优先级高于解引用运算符优先级。 一般不要在结构体成员运算符和结构体指针运算符的量变加上空格,有助于强调包含这些运算符的表达式本质上是一个单一变量名。

在函数中使用结构体

将结构体传递给函数有三种方式,传递结构体的个别成员,传递整个结构体,传递一个指向结构体的指针。当结构体或者结构体的个别成员被传递给一个函数时,它们是按值传递的方式被传递的。当结构体的个别成员或者整个结构体被传递给一个函数时,它们是按值传递的。而以传地址方式传递一个结构体,实际上是将结构体变量的地址传递给被调函数。结构体数组和其他数组一样,都是自动以传地址传递的。

typedef的使用

typedef提供了一种为已经定义好的数据类型创建同义词(或别名)的机制。如下:

typedef struct card Card;

就为结构体struct card定义了一个同义词Card作为该类型的新名字。通常使用typedef,那样就不再需要结构体标记了。如下:

typedef struct { int a; int b; } Node;

直接创建了一个结构体类型Node而不需要另外再编写一条单独的typedef。然而若省略结构体标记而使用typedef如下的自引用结构体,并不能编译通过:

typedef struct { int a; Node *next; //error:unknown type name 'Node' } Node;

typedef是为一个已经存在的数据类型,创建一个作为其别名的新类型名。type还常常被用来为基本数据类型创建一个别名,以便可移植在其余系统上。

位域

由于结构体中成员并不一定是连续的存储在内存单元中的,所以不能用==或者!=来对结构体进行比较。一些结构体的存储区域可能出现一些空洞,因为计算机是按照一定边界来存储不同数据类型的变量的(如“半字”,“字”或“双字”)。计算机中存储数据的基本单位是一个字,通常是2或4个字节。对于空洞的值没有专门的定义,所以即使结构体中数据完全相同,也不能保证空洞中的值完全相同。 当结构体中有无符号整型或者有符号整型成员时,C语言允许指定这些成员所占用的存储位数(bit),被称为位域(bit field)。通过将数据存储在它们所需的最小的存储空间内,可以有效的提高存储空间的利用率。位域的使用如下:

struct bitCard { unsigned face : 4; unsigned suit : 2; unsigned color : 1; };

声明一个位域的方法是,在无符号整数或者有符号整数后加上一个冒号(:)和一个整型常数,这个整型常数表示位域的宽度,这个表示宽度的常数必须是一个整数,它的取值范围是0到系统中正常存储一个整型数所需的二进制位数之间,包含该范围的边界值。 还可以指定一个无名位域,用来填充(padding)结构体中的存储缝隙,如下:

struct example { unsigned a : 13; unsigned : 19; unsigned c : 4; };

填充的目的是为了使成员b能够存储在下一个存储单元中。 宽度为0的无名位域可以用来使下一个位域对齐在一个新的存储单元的边界上。如下:

struct example { unsigned a : 13; unsigned : 0; unsigned c : 4; };

就通过一个宽度为0的无名位域,来跳过存储a的那个存储单元中剩余的二进制位(有多少位就跳多少位),将b对齐在下一个存储单元上。位域操作依赖机器。试图去获取一个位域的地址是一个语法错误。尽管位域可以节约存储空间,但是它们会使编译器生成运行速度较慢的机器代码,是一种以空间换时间的方法。