个人主页: 古德猫宁-
专栏:深入理解计算机系统
C语言基础习题
C语言笔记
目录
结构的声明
结构体变量的创建和初始化
结构体变量的创建
结构体的初始化
结构的特殊声明
访问结构成员
结构体的自引用
引子:假如我们要设计一个程序来打印一份图书目录,打印每本书的各种信息:书名、作者、出版社、版权日期、页数和价格等。其中的一些项目(书名,作者)可以存储在字符数组中,其他项目需要一个int数组或者float数组来存储(价格,页数)。这样用七个不同的数组分别记录每一项的内容比较麻烦,且需要开辟许多空间,特别是要创建多份列表的时候。
所以,我们需要一种既可以包含字符串又能包含数字的数据形式,而且还要保持各信息的独立,C语言中的结构体就能满足你的需求,接下来让我来介绍结构体是什么,有什么用,怎么用。
结构声明描述了一个结构的组织布局,声明如下:
struct tag{member- list ;}variable- list ;
假设要创建一个图书目录的结构:
struct book
{
char title[20];//书名
int value;//价格
char author[20];//作者
//…………
};
该声明描述了一个由俩个字符数组和一个int类型变量组成的结构。该声明并未创建实际的数据对象,只是描述了该对象由什么组成。(有时候我们把结构声明成为模板,因为它勾勒出结构是如何存储数据的)
接下来解释上面的一些细节,首先是关键字struct,它表明跟在其后的是一个结构,后面是一个可选的标记(上面是book),后面的程序中可以使用该标记引用该结构。所以,我们在后面的程序中可以这样声明:
struct book library
这里libiary声明为一个使用book结构布局的结构变量。
在结构声明中,用一对花括号括起来的是成员列表。每个成员都用自己的声明来描述,成员可以是任意一种C语言的数据类型
最后括号后面的分号是必需的(不能省略),因为它表示结构布局定义结束。
声明完结构之后,我们就要来创建结构体变量和初始化了
首先,结构有两层含义,一层含义是“结构布局”,结构布局告诉编译器如何表示数据,但是它并未让编译器为数据分配空间,所以接下来就是创建一个结构变量,即是结构的另一层含义。
程序中创建结构变量的一行是:
struct book library
编译器执行这行代码便会创建一个结构变量library。编译器使用book模板为该变量分配空间:
一个内含20个元素的字符数组title,一个内含20个元素的字符数组author,一个int类型变量
这些存储空间都与一个名称library结合在一起。
例如下图:
在结构声明的变量中,struct book 所起的作用相当于一般声明中的int或float。
例如:可以定义两个struct book类型的变量,或者是指向struct book 类型结构的指针:
struct book
{ //结构体成员
char title[20];//书名
int value;//价格
char author[20];//作者
};
struct book library1;//定义结构体变量1
struct book library2;//定义结构体变量2
struct book *pst;//定义指向结构体的指针
pst = &library;//将指针指向结构体变量
struct book
{
char title[20];//书名
int value;//价格
char author[20];//作者
//…………
};
int main()
{
//按照结构体成员的顺序来初始化
struct book library = { "C Primer Plus",99,"史蒂夫-普拉达" };
//未按照结构体成员的顺序来初始化
struct book library = { .author = "史蒂夫 - 普拉达",.title = "C Primer Plus" ,.value = 99 };
return 0;
}
当然,我们也可以通过将指针指向结构体变量的地址,使用指针来访问和修改结构体的成员;
#include
struct book
{ //结构体成员
char title[20];//书名
int value;//价格
char author[20];//作者
};
int main()
{
struct book library;//定义结构体变量
struct book* pst = &library;//定义指向结构体的指针,将指针指向结构体变量
pst->value = 30;//通过指针修改结构体成员
return 0;
}
在声明结构的时候,可以不完全声明
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}s[20], *p;//这里表示创建了一个包含20个元素的结构体数组s,每个元素包含一个整数a,一个字符b,一个浮点数c
//同时定义了一个指向这个结构体类型的指针p
上面的结构体在声明的时候省略掉了结构体标签(tag),称为匿名结构体
注意:
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。
编译器会把上面的两个声明当成完全不同的两个类型。
//所以在上面的两个结构体代码基础上,下面的代码是非法的
p=&x;
结构类似于一个“超级数组”,这个超级数组中,可以是一个元素为char类型,下一个元素为float类型,下一个元素为int数组。我们可以通过数组下标单独访问数组中的各元素,那么,如何访问结构中的成员?
使用结构成员运算符——点(.)访问结构中的成员。
例如:
struct book
{
char title[20];//书名
int value;//价格
char author[20];//作者
//…………
};
int main()
{
//按照结构体成员的顺序来初始化
struct book library = { "C Primer Plus",99,"史蒂夫-普拉达" };
//未按照结构体成员的顺序来初始化
struct book library = { .author = "史蒂夫 - 普拉达",.title = "C Primer Plus" ,.value = 99 };
printf("title:%s\n", library.title);
printf("value:%d\n", library.value);
printf("author:%s\n", library.author);
return 0;
}
在结构体中包含一个类型为该结构本身的成员是否可以呢?
比如,定义一个链表的节点;
struct Node
{
int data;
struct Node next;
};
上述的代码是否正确,如果正确的话,那sizeof(struct Node)是多少
仔细分析,其实是不行的,因为一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的。
正确的自引用方式:
struct Node
{
int data;
struct Node* next;
};
在结构体自引用使用的过程中,夹杂了typedef对匿名结构体类型重命名,也容易引入问题,看看下面的代码有无错误:
typedef struct
{
int data;
Node* next;
}Node;
答案是不行的,因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。
解决方案如下:定义结构体不要使用匿名结构体了
typedef struct Node
{
int data;
struct Node* next;
}Node;
本章是对于C语言结构体的简介,下篇将持续介绍有关结构体的内容
感谢各位观看