
复杂的数据类型
一般的步骤:
1、声明模板
2、定义变量,分配内存空间
3、初始化
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
标记、标识符、标签
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构:
设计程序很重要的一步是提高表示数据的能力,选择表示数据的方法。C提供了结构变量(structure variable)提高你表示数据的能力。
14.1 建立结构声明
结构声明(structure variable)描述了一个结构的组织布局。声明类似如下:
struct book{
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
该声明并未创建实际的数据对象,只描述了该对象由什么组成。(结构声明有时候称为模板)
关键字struct,表明跟在其后的是一个结构,后面是一个可选的标记。后面的程序可以根据标记引用该结构。如果打算重复使用该模板,就要使用带标记的形式。
用花括号括起来的是结构成员列表。
花括号后面的分号是必须的,表明结构布局定义结束。
14.2 定义结构变量
结构有两层含义。一层含义是“结构布局”,结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间。
下一步是创建一个结构变量,即是结构的另一层含义。
例如 struct book library;
编译器执行这行代码便创建了结构变量library。编译器使用book模板为该变量分配空间。解释:一个内含MAXTITL个元素的char数组,一个内含MAXAUTL个元素的char数组和一个float类型的变量;这些存储空间都与一个名称library结合在一起。struct book的作用相当于声明了一个int或float。
struct book相当于自定义了一种数据类型,这个数据类型用book作标记。程序可以根据标记引用该数据类型。
就计算机而言,声明:struct book library;
是以下声明的简化:
struct book{
char title[MAXTITL];
char author[AXAUTL];
float value;
}library;
声明结构的过程和定义结构变量的过程可以组合成一个步骤。
14.2.1 初始化结构
使用在一对花括号括起来的初始化列表进行初始化。各初始化项用逗号分隔。让每个成员的初始化项独占一行,这样做只是为了提高代码的可读性。对编译器而言,只需要用逗号分隔各成员的初始化项即可。
14.2.2 访问结构成员
结构类似于超级数组。超级数组中,可以是一个元素为char类型,一个元素为forat类型,一个元素为int数组。
数组可以通过数组下标单独访问数组中的各元素。
访问结构中的成员,可以使用结构成员运算符---->点(.)访问结构中的成员。
例如library.value即访问library中的value部分。可以像使用任何float类型变量那样使用library.value。library是一个结构,library.value是一个float类型的变量。
14.2.3 结构的初始化器
C99和C11为结构指定了初始化器。
struct book gift={
.value = 25.99,
.author = “James Broadfool”,
.title = “Rue for the Toad”
};
进一步了解结构的一些相关类型。
14.3 结构数组
14.3.1 声明结构数组
struct book library[MAXBKS];
14.3.2 标识结构数组的成员
library[0].value
library[4].title
14.3.3 程序讨论
14.4 嵌套结构
一个结构中包含另一个结构;
14.5 指向结构的指针
好处:1、指向结构的指针比结构本身更容易操控,就像指向数组的指针比指针本身更容易操控;2、早期C中,结构不能作为参数传递给函数,可以传递指向结构的指针;3、即使能传递结构,传递指针更有效率;4、一些用于表示数据的结构中包含指向其他结构的指针。
14.5.1 声明和初始化结构指针
声明结构指针:struct guy * him;
首先是关键字struct,其次是结构标记guy,然后是一个星号*,然后跟着指针名him;
结构名不是结构的地址,因此要在结构名前面加上&运算符;
例如fellow是结构数组,fellow[0]是一个结构,要让him指向fellow[0],可以这样写:
him = &fellow[0];
14.5.2 用指针访问成员
使用 ->运算符,访问结构中的成员;
him == &barney , 那么 him ->income 即是 barney.income
him == &fellow[0] , 那么 him ->income 即是 fellow[0].income
14.6 向函数传递结构的信息
ANSI C允许把结构作为参数使用。所以程序员可以选择传递结构本身,还是传递指向结构的指针。如果你只关心结构中某一部分,你可以把结构的成员作为参数。
14.6.1 传递结构成员
直接传递结构成员,其实被调函数根本不关心传递的实际参数是否是结构成员。
14.6.2 传递结构的地址
double sum(const struct funds *);
传递结构stan给函数,必须使用&运算符来获取结构的地址。&stan然后传递给函数。
14.6.3 传递结构
函数的形参直接是结构类型的形参,实参就是结构变量。
14.6.4 其他结构特性
结构允许把值赋值给另一个结构。
现在的C不仅能把结构本身作为参数传递,也能把结构作为返回值返回。
把结构作为函数参数可以把结构的信息传达给函数。
把结构作为返回值的函数能把结构的信息从被调函数返回主调函数。
结构指针允许这样的双向通信。
14.6.5 结构和结构指针的选择
把指针作为参数传递优点:执行起来很快,能兼容旧的和新的C,只需要传递一个地址。缺点是:无法保护数据。被调函数的某些操作可能会意外影响原来结构中的数据。不过ANSI C新增的限定符const解决了这个问题。
把结构作为参数传递的优点是:函数处理的是原始数据的副本,这保护了原始数据。另外,代码风格也更清楚。缺点是:较老版本可能无法处理这样的代码,传递结构浪费时间和存储空间,尤其是把大型结构传递给函数。
通常做法:为了追求效率使用结构指针作为函数参数,如需防止原始数据被意外修改,使用const限定符。按值传递结构是处理小型结构的最常见方法。
14.6.6 结构中的字符数组和字符指针
字符数组存储字符串
struct names{
char first[LEN];
char last[LEN];
};
指向char的指针来存储字符串
struct pnames{
char * first;
char * last;
};
struct names veep = {“Talia”,“Summers”};
struct pnames treas = {“Brad”,“Fallingjaw”};
对于struct name类型的变量veep来说,以上字符串都储存在结构内部;这种做法比较占空间。
对于struct pnames类型的变量treas来说,字符串储存在编译器储存常量的地方。结构本身只储存了两个地址。
如果pnames类型的变量treas未经初始化,地址可以是任何值,这一操作可能导致程序奔溃。
因此,如果要用结构储存字符串,用字符数组比较简单。用指向char的指针也行,但是误用会导致严重问题。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
联合:
联合(union)是一种数据类型,它能在同一个内存空间中储存不同的数据类型(不是同时储存)。
典型用法:设计一种表以储存既无规律、事先也不知道顺序的混合类型。使用联合类型的数组,其中的联合都大小相等,每个联合可以储存各种数据类型。
创建联合和创建结构的方式相同,需要一个联合模板和联合变量。
先定义一个带标记的联合模板;
union hold{
int digit;
double bigfl;
char letter;
}
声明一个单独的联合变量fit,会按照占用空间最大的数据类型分配空间;
union hold fit; //声明一个联合变量fit;\
联合的初始化:只能初始化一个值。
14.9.1 使用联合
在联合中,一次只储存一个值。即使有足够的空间,也不能同时储存一个char类型和一个int类型。
fit.digit = 23; //把23储存在fit,占2个字节
fit.bigfl = 2.0; //清除23,储存2.0,占8个字节
fit.letter = ‘h’; //清除2.0,储存h,占1字节
用指针访问结构成员使用->运算符;
同样,用指针访问联合时也要使用->运算符;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
枚举:
枚举类型(enumerated type)用符号名称来表示整型常量。使用enum关键字。
枚举的目的是提高程序的可读性。语法与结构的语法相同。
声明:
enum spectrum {red, orange, yellow, green, blue, violet};
enum spectrum color;
第一条声明:spetrum是标记,允许把enum spectrum作为一个类型名使用。花括号内的标识符枚举了spectrum变量可能有的值。这些符号常量叫做枚举符。
第二条声明:color作为该类型的变量。
enum常量:
red=0,orange=1。从技术层面看,这些枚举符都是int类型的常量。
默认值:
默认情况下,枚举列表中的常量都被赋予0、1、2等。
赋值:
在枚举声明中,可以为枚举常量指定整数值。
enum level {low =100, medium =500, high=2000};
考虑以下情况:
enum feline {cat, lynx =10, puma, tiger};
那么cat的值是默认0,puma是11,tiger是12;
enum的用法:
枚举类型的目的是增强程序的可读性和可维护性。
注意,枚举类型只能在内部使用,如果要输入color中orange的值,只能输入1。而不是单词orange。或者,让程序先读入orange,在将其转换为orange代表的值。
枚举类型是整数类型,所以可以在表达式中以使用整数变量的方式使用enum变量。它们用在case语句中很方便。
共享名称空间:
C语言使用名称空间(namespace)来标识程序中的各部分,即通过名称来识别。作用域是名称空间概念的一部分:两个不同作用域的同名变量不冲突,两个相同作用域的同名变量冲突。
在特定作用域中,结构标记,联合标记,枚举标记都共享相同的名称空间,该名称空间与普通变量使用的空间不同。这意味着在相同作用域中变量和标记的名称可以相同,不会引起冲突。但是不建议使用相同的标识符,以免引起混乱。
注意,在C++中,把标记和变量名放在相同的名称空间中。