(十三)结构体类型
前面我们讲的数据类型都是一些非常基本的数据类型,比如整型、字符型、浮点型等,之所以要确定数据的类型,是因为要确定在内存中给这些数据留多大的空间,以及这些数据如何存储在给定的空间里。比如,如果你把97赋值给一个整型变量,那内存中存储97这个数字,就应该是4个字节的空间大小,而且是以补码的形式存储的。如果你把97赋值给一个字符型变量,那系统就会在内存中给97分配1个字节的空间去存放,而且是以小写字母a的SCILL码的二进制形式存储的。
但是,这些数据类型都太太细颗粒了,很难适应需求。比如,现在我们要存放一些书籍的名字,每本书都要存放名称、作者、价格、出版日期、出版社、ISBN号等信息,整型字符型等这些基本类型就显得太繁琐了,当然有人说用数组存放,但是数组里面的元素类型都是一样的啊,这里名称和作者是字符型,那价格又是整型,元素的类型不一致呀。所以结构体数据类型就诞生了,结构体可以存储不同类型的数据。
1、结构体声明
- struct 不能省,是结构体数据类型的标志。
- 结构体名称一般约定俗成的是首字母大写。全部大写是宏定义的名字,全部小写是普通变量的名字。
- 末尾分号不能省。
说明:结构体声明只是进行一个框架的描述,并不会在内存中分配存储空间,直到我们定义了一个真正的结构体变量的时候才会在内存中分配空间。
2、定义结构体类型变量
struct 结构体名称 结构体变量名
3、访问结构体变量
要访问结构体成员,我们需要引入一个新的运算符:点号 . 。
比如book.title就是引用book结构体的title成员,它是一个字符数组。而book.price则是引用book结构体的price成员,它是一个浮点型的变量。
4、初始化结构体变量
5、调整结构体成员的顺序,可以达到节省内存空间的目的
从代码上看,变量a应该是6各字节,但为什么返回了12各字节。这是因为编译器对结构体成员进行内存对其后的结果。内存对其可以让cpu更快的处理数据,所以要内存对其。如果我们修改上面程序的代码如下:
是不是内存变成了8。下面图示这两各程序是如果内存对齐的:
至于为什么,可以参考文章:被遗忘的C结构体打包技术
6、结构体嵌套
套娃无止境。比如上面例子中的出版日期,我们可以单独再申请一个结构体单独存储出版日期:
7、结构体数组
排列组合也无止境。结构体数组是“结构体的数组”,是一个数组。上面我们把一本书的信息放到一个结构体变量中,那我们有100本书呢,是不是搞一个数组就可以啊。只不过这里每个数组元素不再是之前的基本数据类型,而是一个结构体类型。所以每个数组元素都包含该结构体的所有成员。
(1)定义结构体数组
(2)初始化结构体数组
定义的时候就可以初始化,例如下图示例:
8、结构体指针
哪里也少不了指针,指针是灵魂。结构体指针是"结构体的指针",本质是各指针,这个指针指向一个结构体变量。也就是这个指针的值是结构体变量的存放地址。
-
定义一个结构体指针,例如:
struct Book *pt; 表示你申请一个指针变量pt,pt指向结构体Book。
pt = &book; 这里给指针赋值,赋值的时候一定赋的是结构体变量book的地址。 -
通过结构体指针访问结构体成员:
方法1:(* 结构体指针).成员名 ,例如,(*pt).title ,就是先对指针解引用,然后就和普通的结构体变量一样,可以.成员名了。说明,这里.的优先级(1级)是高于*号(2级)的,所以*pt要用小括号括住。 . 号运算符应用于对象。
方法2:结构体指针->成员名。例如,pt -> title , 其中箭头(->)中间不能有空格,是一体的,表示成员选择符,用于指针。
9、传递结构体变量:结构体变量作为函数的参数及返回值
我们知道,函数调用的时候,函数参数的传递就是一个值传递的过程,也就是将实参赋值给形参的过程。那么,当一个函数的参数是结构体变量时,函数参数的传递就要求:形参结构体成员的类型和实参结构体成员的类型一致,才能顺利传参。
而结构体变量作为函数的返回值也是再正常不过了。下面代码示例展示,如何将结构体变量作为参数及返回值的用法:
10、传递指向结构体变量的指针
从程序执行效率上考虑,结构体变量的尺寸有时可以很大,那函数调用的时候,在时间和空间上的开销就会很大。所以,在最开始的时候,C语言是不允许直接将结构体作为参数来传递给函数的。但是现在C语言取消了这个限制,结构体变量可以作为参数直接传递给函数。但是作为开发者你不得不考虑这个开销。那为了避免这个开销,我们可以用一个指针,指向这个结构体,那我们只需要传递指针变量即可。而一个指针变量只有8个字节,是不是轻量了许多。 按照这个思路我们更该上面的代码:
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date riqi;
char publisher[40];
};
struct Book *inputbookinfo(struct Book *input);
void printbook(struct Book *book);
struct Book *inputbookinfo(struct Book *input){
printf("请输入书名:");
scanf("%s", input->title);
printf("请输入作者:");
scanf("%s", input->author);
printf("请输入价格:");
scanf("%f", &(input->price));
printf("请输入出版日期,年月日用空格区分:");
scanf("%d%d%d",&input->riqi.year, &input->riqi.month, &input->riqi.day);
getchar();
printf("请输入出版社名称:");
scanf("%s", input->publisher);
return input;
}
void printbook(struct Book *b){
printf("%s\n%s\n%.2f\n%d-%d-%d\n%s\n",(*b).title, (*b).author, b->price, b->riqi.year, b->riqi.month, b->riqi.day, b->publisher);
}
int main(void)
{
struct Book b1,b2;
printf("请录入第一本书的信息:\n");
inputbookinfo(&b1);
putchar('\n');
printf("请录入第二本书的信息:\n");
inputbookinfo(&b2);
printf("\n你录入完毕,你录入的书籍信息如下:\n");
printbook(&b1);
putchar('\n');
printbook(&b2);
return 0;
}
11、动态申请结构体
使用malloc函数为结构体分配存储空间。代码如下:
#include <stdio.h>
#include <stdlib.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date riqi;
char publisher[40];
};
struct Book *inputbookinfo(struct Book *input);
void printbook(struct Book *book);
struct Book *inputbookinfo(struct Book *input){
printf("请输入书名:");
scanf("%s", input->title);
printf("请输入作者:");
scanf("%s", input->author);
printf("请输入价格:");
scanf("%f", &(input->price));
printf("请输入出版日期,年月日用空格区分:");
scanf("%d%d%d",&input->riqi.year, &input->riqi.month, &input->riqi.day);
getchar();
printf("请输入出版社名称:");
scanf("%s", input->publisher);
return input;
}
void printbook(struct Book *b){
printf("%s\n%s\n%.2f\n%d-%d-%d\n%s\n",(*b).title, (*b).author, b->price, b->riqi.year, b->riqi.month, b->riqi.day, b->publisher);
}
int main(void)
{
struct Book *b1, *b2;
b1 =(struct Book *)malloc(sizeof(struct Book));
b2 = (struct Book *)malloc(sizeof(struct Book));
if(b1==NULL || b2==NULL){
printf("内存分配失败!\n");
exit(1);
}
printf("请录入第一本书的信息:\n");
inputbookinfo(b1);
putchar('\n');
printf("请录入第二本书的信息:\n");
inputbookinfo(b2);
printf("\n你录入完毕,你录入的书籍信息如下:\n");
printbook(b1);
putchar('\n');
printbook(b2);
free(b1);
free(b2);
return 0;
}
12、案例:构建一个图书馆
让用户将所有的书籍信息都录入到系统里。
#include <stdio.h>
#include <stdlib.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date riqi;
char publisher[40];
};
struct Book inputbookinfo(struct Book input);
void printbook(struct Book book);
struct Book inputbookinfo(struct Book input){
printf("请输入书名:");
scanf("%s", input.title);
printf("请输入作者:");
scanf("%s", input.author);
printf("请输入价格:");
scanf("%f", &(input.price));
printf("请输入出版日期,年月日用空格区分:");
scanf("%d%d%d",&input.riqi.year, &input.riqi.month, &input.riqi.day);
getchar();
printf("请输入出版社名称:");
scanf("%s", input.publisher);
return input;
}
void printbook(struct Book b){
printf("%s\n%s\n%.2f\n%d-%d-%d\n%s\n",b.title, b.author, b.price, b.riqi.year, b.riqi.month, b.riqi.day, b.publisher);
}
int main(void)
{
int i,flag;
int count =1;
struct Book *b=NULL;
while(1){
printf("开始录入第 %d 本书,输入数字 %d 进入录入状态,输入其他数字结束录入:", count,count);
scanf("%d", &flag);
getchar();
if(flag!=count){
printf("\n\n录入结束。");
break;
}
else{
b = (struct Book *)realloc(b, count*sizeof(struct Book));
if(b==NULL){
printf("内存分配失败!\n");
exit(1);
}
b[count-1]=inputbookinfo(b[count-1]);
putchar('\n');
count++;
}
}
printf("你录入的书籍信息如下:\n");
for(i=0;i<count-1; i++){
printbook(b[i]);
putchar('\n');
}
free(b);
return 0;
}