本篇的初衷原本是想要写链表前提一嘴结构体...结果越提越多(emm,写完发现篇幅已经很大了。
所以链表会另开一篇博客,本篇主要探讨数据的一些巧妙处理方式。
目录
一、结构体
在说链表前提一下结构体,因为结构体相当于一种数据类型,而我们可以使用结构体来实现链表操作(下篇讲)。
1.1结构体的概念
有时候我们需要一个宏观的结构去包罗万象,比如想要建立一个班级成员信息表,里面包括每个人的姓名、性别、家庭住址、出身年月等等,你会发现这些信息如果想要存储在计算机中,他们的数据类型都不一样,并且我们也不希望一个个去定义具体每一项的数据类型。那么这时候结构体就派上用场了。
结构体是一种即能包含数字又能包含字符串的数据结构,并且每个数据之间能够做到包含在一个结构中却又相互独立。不太严谨地说,其实结构体类似于一个“超级数组”。
1.2 结构体的声明
struct A {
float B;
int C;
char D[ 10 ];
};
这是一个由一个int类型变量、一个float类型变量和一个字符数组组成的结构。该声明实际上是创建了一个名为struct A的新类型,而它没有创建实际的数据对象,只是描述了该对象由什么组成。
记得结构体声明需要放在每段代码int main()前,并且记得花括号后的“ ;”(它表示结构局部定义结束)。
其中重点介绍一下关键字“struct”。它声明了一个结构体,后跟为一个可选的结构体标记(例中为A)。
当然,你也可以选择不带标记,像这样的匿名形式:
struct {
float B;
int C;
char D[ 10 ];
};
然而如果你打算多次使用这个结构模板,就要使用带标记的形式。
1.3 创建结构变量以及初始化结构
格式为:
struct A book;
它把book声明为一个使用A结构布局的结构变量。
如果你想声明三个这个结构的变量,你就可以:
struct A book, ptbook, *pt;
直接在后面接就好。看,你不仅可以声明变量,你还可以声明指向结构的指针。
关于结构体的初始化,为下面操作:
struct A { 1.33, 11, "good day" };
很简单的初始化操作方法,在这里不多描述。
1.4 访问结构成员的两个运算符:->和.
-> | 指针运算符 |
. | 成员符号 |
关于对结构体内部成员的访问,我们使用(. )运算符来完成。(注意:.运算符只能跟在结构后面)
比如 A.B ,就是访问结构A中的B部分。因此我们也可以有类似于这样的操作:
printf(" %f ", A.B);
如果你想进一步初始化结构中指定的成员,就可以使用点运算符+成员名:
struct A book={ .B = 1.33 };
再者就是当你想要用指针访问结构成员时,可以使用( -> ) 来完成。
比如我们声明一个结构指针:
struct A *vim;
同时我设boy是一个A类型的结构变量:
struct A boy;
其中B是boy中的一个成员,此时如果我要让指针vim指向成员boy,即:
vim == &boy;
那么 vim -> B 表示的就是 boy .B
由此看来两个运算符的工作方式都是相同的,只不过面向的对象略有差异。
获取结构地址时,必须使用&运算符。和数组名不同,结构变量名不是其地址的别名。
1.5 结构数组
关于结构数组,要注意的点是其声明:
struct A book[ 10 ];
注意book此时是数组名而不是结构名,它表示该数组中的每个元素都是struct A类型的结构变量。
同时注意数组元素下标应该跟在book后面而不是成员名后面:
book .B[ 2 ] //错误
book[ 2 ].B //正确
book[ 2 ] .D[ 4 ] //代表数组中book[ 2 ]元素的D成员的一个字符
1.6 伸缩型数组成员(C99)
我对于伸缩型数组成员的用法理解是更具有弹性空间,反复使用能够更加灵活。
下面是关于伸缩型数组成员的一些规则:
1、不能立刻使用;
2、伸缩型数组成员必须是结构中的最后一个成员;
3、伸缩数组的声明其实类似于普通数组,只不过它的方括号是空的。
举个例子:
struct A
{
int high;
double weight;
double scores[ ]; //伸缩型数组成员
}
刚开始声明一个struct A类型的结构变量时,我们无法用scores做任何事情,因为目前还没有给这个数组预留内存中相对应的存储空间。
此时声明一个指向struct A类型的指针,然后用malloc()来分配足够的空间以存储此结构的常规内容和伸缩型数组成员所需的额外空间。
比如此时设伸缩型数组内含6个double类型值,我们可以:
struct A *point; //声明一个指针
point = malloc( sizeof( struct A ) + 6 * sizeof( double ) ); //malloc请求为一个结构和一个数组分配存储空间
现在就有足够的内存空间存储它们了,接下来我们可以用指针访问结构成员了:
point -> high = 11; //设置成员值
point -> scores[ 4 ] = 6.5; //访问数组元素中的一个元素
同时带伸缩型数组成员的结构有一些限制:
1、不能用结构进行赋值;
2、不要传值,要传地址;
3、不要使用带伸缩型数组成员的结构作为数组成员或另一个结构的成员。
二、 联合
既然上面介绍了结构体,那也顺带提一下联合。
联合也是一种数据类型,其声明和结构声明也很像:
union A
{
int high;
double weight;
double scores;
}
其中关键字“ union "表明它是一个联合体。
是不是乍一看和结构体的声明也只是关键字之差而已,但实际上两者有着很大区别:虽说结构体和联合体都是由多个不同类型的成员组成的,但联合中只存储一个被选中的成员,而结构的所有成员都存在。
我的理解就是说,结构体相当于每个成员都能有属于自己的一块专属内存位置,而联合体是多个成员共享一块内存位置,来了新的成员就要把原来的东西全部清走(归零)。
三、枚举类型
枚举类型实质上是一种整型数据类型,主要通过预定义列出所有值的标识符来定义一个有序集合。和预定义#define一样,使用枚举类型时应该放在int main()之前。
3.1 枚举声明
enum week {Mon,Tue,Wed,Thu,Fri,Sat,Sun};
其中关键字“ enum ”表示枚举类型身份。花括号中是一个枚举集合,第一个元素的默认值为0,后面元素的值依次递增,简单代码展示:
输出结果为:
如果此时为初始化中间一个元素的值为9:
输出结果为:
这种情况你会发现出现断层,赋值前的成员依旧按照默认赋值,而被赋值的值往后的成员都从所赋值数开始相加。
3.2 枚举用法
1、常量
可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法,代码更短更简洁。
2、switch case语句
switch语句使用枚举,能让我们的代码可读性更强。(数字与名字的区别)
- 注意:同一个程序中不能定义同名的枚举类型,不同的枚举类型中也不能存在同名的命名常量
四、你想取个外号吗:typedef
4.1 typedef简介
typedef工具可以用于建立C标准数据类型的别名或缩写。刚开始我觉得这个操作多少有些多此一举,但它用于给复杂的类型命名时确实能简化程序、提高可移植性。
其基础使用格式为:
typedef <标准类型> <你想命的名>
注意一点,typedef只能用于创建类型别名,不能用于值。
简单使用示例1:
typedef char NAME[ 20 ];
//此时“NAME”为字符数组类型,而非数组名
简单使用示例2 :
typedef unsigned char BYE;
//此时unsigned char == BYE,都表示同种数据类型
BEY x, y[ 10 ], *n; //然后你就可以用别名定义变量了
4.2 typedef与#define预定义的区别
那么这时候就有人说了:哎?这个我用#define宏定义预处理一样可以完成啊:
#define BYE unsigned char //和typedef处理是同样的效果
那雀食。但你要知道typedef也有#define没有的功能:
对于指针:
typedef char *BE;
如果没有typedef关键字,编译器将会将BE当作是一个指向char类型的指针变量。但加了typedef关键字后,它就将BE设置为指向char类型指针的类型。此时我们就可以有:
BE size,high; ==> BE *size, *high;
但如果我们使用#define预定义:
#define BE char*
然后声明:
BE size,high; ==> BE *size,high;
这会导致只有size被定义为指针。
选择表示数据的方法有很多,希望自己在以后的代码编写中,能够充分利用好各个数据处理方法的优点,从而使得其效率更高,会感觉是一件很有趣的事情。
下篇链表。