【C】结构体
结构体
结构体是C语言中一种重要的数据类型,该数据类型由一组称为成员(或称为域,或称为元素)的不同数据组成,其中每个成员可以具有不同的类型。结构体通常用来表示类型不同但是又相关的若干数据。
结构体声明
struct 结构体名称
{
结构体成员1;
结构体成员2;
结构体成员3;
...
};
其中,结构体成员既可以是任何一种基本的数据类型,也可以是另一个结构体(相当于结构体的嵌套)。
注意:结构体声明你即可以放在所有函数的外面,也可以单独在一个函数里面声明。如果是后者,则该结构体只能在该函数中被定义。
例:一本书的结构体声明如下
struct Book
{
char title[128]; //书品
char author[40]; //作者
float price; //价格
unsigned int date; //出版日期
char publisher[40]; //出版社
};
注:结构体名称一般采用首字母大写的形式
定义结构体类型变量
结构体声明只是进行一个框架的描绘,它并不会在内存中分配空间存储数据,直到你定义一个结构体类型的变量。
struct 结构体名称 结构体变量名
例:
#include<stdio.h>
struct Book //全局结构体,所有函数都能看到
{
char title[128]; //书品
char author[40]; //作者
float price; //价格
unsigned int date; //出版日期
char publisher[40];
};
int main(void)
{
struct Book book; //定义局部结构体变量
return 0;
}
访问结构体变量
- 要访问结构体成员,我们需要引入一个新的运算符——点号(.)运算符。
- 比如
book.title就是引用 book 结构体的 title 成员;而book.price则是引用 book 结构体的 price 成员。
#include<stdio.h>
struct Book //全局结构体,所有函数都能看到
{
char title[128]; //书品
char author[40]; //作者
float price; //价格
unsigned int date; //出版日期
char publisher[40];
} book; //全局结构体变量book
int main(void)
{
printf("请输入书名 : ");
scanf("%s",book.title);
printf("请输入作者 : ");
scanf("%s",book.author);
printf("请输入售价 : ");
scanf("%s",&book.price);
printf("请输入出版日期 : ");
scanf("%s",&book.date);
printf("请输入出版社 : ");
scanf("%s",book.publisher);
printf("\n=====数据录入完毕=====");
printf("书名 : %s\n", book.title);
printf("作者 : %s\n", book.author);
printf("售价 : %.2f\n", book.price);
printf("出版时间 : %d\n", book.date);
printf("出版社 : %s\n", book.publisher);
return 0;
}
初始化结构体
初始化结构体变量
可以在定义结构体变量的时候同时为其初始化:
struct Book book = {
"《带你学C带你飞》",
"小甲鱼",
48.8,
20171111,
"清华大学出版社"
};
初始化结构体的指定成员值
-
C99 增加了一种新特性:支持初始结构体的指定成员值。
-
其语法和数组指定初始化元素类似,只不过结构体指定初始化成员使用点号(.)运算符和成员名(数组则是用方括号和下标索引值)。
例:我们可以让程序只初始化 Book 的 price 成员:
struct Book book = {.price = 48.8};
-
利用该特性,还可以不按结构体声明的成员顺序进行初始化:
struct Book book = { .publisher = "清华大学出版社", .price = 48.8, .date = 20171111 };
内存对齐
#include<stdio.h>
int main(void)
{
struct A
{
char a; //1个字节
int b; //4个字节
char c; //1个字节
} a = {'x',520,'o'};
printf("size of a = %d\n",sizeof(a));
return 0;
}
输出结果:
size of a = 12
原因:编译器对结构体成员进行内存对齐

如果对结构体成员进行顺序调整:
struct A
{
char a; //1个字节
char c; //1个字节
int b; //4个字节
} a = {'x',520,'o'};
printf("size of a = %d\n",sizeof(a));
则输出结果:
size of a = 8
原因:

结构体嵌套
结构体可以进行嵌套声明
例:
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;
char publisher[40];
};
要访问其结构体成员的话,就需要使用两层点号(.)运算符来进行操作。
因此,试图用 book.date 访问日期的做法是错误的,你只能通过 book.date.year,book.date.month 和 book.date.day 依次打印出年月日。
结构体数组
结构体数组跟之前我们学习过数组概念一致,只不过是每个数组元素不再是简单的基础类型,而是一个结构体类型的数据。
定义结构体数组和定义一个结构体变量的语法类似。
第一种方法是在声明结构体的时候进行定义:
struct 结构体名称
{
结构体成员;
} 数组名[长度];
第二种方法是先声明一个结构体类型(比如上面 Book),再用此类型定义一个结构体数组:
struct 结构体名称
{
结构体成员;
};
struct 结构体名称 数组名[长度];
结构体指针
指向结构体变量的指针我们称之为结构体指针
struct Book *pt;
这里声明的就是一个指向 Book 结构体类型的指针变量 pt。
我们知道数组名其实是指向这个数组第一个元素的地址,所以我们可以将数组名直接赋值给指针变量。
但结构体变量不一样,结构体的变量名并不是指向该结构体的地址,所以要使用取地址运算符(&)才能获取其地址,如:
pt = &book;
通过结构体指针访问结构体成员有两种方法:
(*结构体指针).成员名结构体指针->成员名
第一种方法由于点号运算符(.)比指针的取值运算符(*)优先级要高,所以要使用小括号先对指针进行解引用,让它先变成该结构体变量,再用点运算符去访问其成员。
相比之下,第二种方法更加方便和直观。不知道大家有没有发现,第二种方法使用的成员选择运算符(->)自身的形状就是一个箭头,箭头具有指向性,所以我们一下子就把它跟指针联系起来。
需要注意的是,两种方法在实现上是完全等价的,所以无论你习惯使用哪一种方法都可以访问到结构体的成员。
但切记,点号(.)只能用于结构体,而箭头(->)只能用于结构体指针,这两个就不能混淆。.
传递结构体变量
两个相同结构体类型的变量可以直接进行赋值:
int main(void)
{
struct Test
{
int x;
int y;
}t1, t2;
t1.x = 3;
t1.y = 4;
t2 = t1;
printf("t2.x = %d, t2.y = %d\n",t2.x,t2.y);
}
输出结果:
t2.x = 3, t2.y = 4
可以看到 t2 = t1; 语句将 t1 这个结构体变量所有成员的值都成功地赋值给了 t2。
前提:两个结构体变量类型要一致
同样的道理,结构体变量也是可以作为函数参数进行传递的:
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;
char publisher[40];
};
struct Book getInput(struct Book book);
void printBook(struct Book book);
struct Book getInput(struct Book book) //定义结构体函数
{
printf("请输入书名:");
scanf("%s", book.title);
printf("请输入作者:");
scanf("%s", book.author);
printf("请输入售价:");
scanf("%f", &book.price);
printf("请输入出版日期:");
scanf("%d-%d-%d", &book.date.year, &book.date.month, &book.date.day);
printf("请输入出版社:");
scanf("%s", book.publisher);
return book;
}
void printBook(struct Book book)
{
printf("书名:%s\n", book.title);
printf("作者:%s\n", book.author);
printf("售价:%.2f\n", book.price);
printf("出版日期:%d-%d-%d\n", book.date.year, book.date.month, book.date.day);
printf("出版社:%s\n", book.publisher);
}
int main(void)
{
struct Book b1, b2;
printf("请录入第一本书的信息...\n");
b1 = getInput(b1); //将b1作为参数传递到结构体函数中
putchar('\n');
printf("请录入第二本书的信息...\n");
b2 = getInput(b2);
printf("\n\n录入完毕,现在开始打印验证...\n\n");
printf("打印第一本书的信息...\n");
printBook(b1);
putchar('\n');
printf("打印第二本书的信息...\n");
printBook(b2);
return 0;
}
传递指向结构体变量的指针
在最开始的时候,C语言是不允许直接将结构体作为参数传递给函数的。
当初这个限制主要是出于对程序执行效率上的考虑。
因为结构体变量的尺寸可以是很大的,那么在函数调用的过程中将会导致空间和时间上的开销也相对是巨大的。
既然传递结构体变量可能导致程序的开销变大,那么应该如何做才好呢?
没错,使用万能的指针!
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;
char publisher[40];
};
struct Book getInput(struct Book book);
void printBook(struct Book book);
void getInput(struct Book *book)
{
printf("请输入书名:");
scanf("%s", book->title);
printf("请输入作者:");
scanf("%s", book->author);
printf("请输入售价:");
scanf("%f", &book->price);
printf("请输入出版日期:");
scanf("%d-%d-%d", &book->date.year, &book->date.month, &book->date.day);
printf("请输入出版社:");
scanf("%s", book->publisher);
}
void printBook(struct Book *book)
{
printf("书名:%s\n", book->title);
printf("作者:%s\n", book->author);
printf("售价:%.2f\n", book->price);
printf("出版日期:%d-%d-%d\n", book->date.year, book->date.month, book->date.day);
printf("出版社:%s\n", book->publisher);
}
int main(void)
{
struct Book b1, b2;
printf("请录入第一本书的信息...\n");
getInput(&b1);
putchar('\n');
printf("请录入第二本书的信息...\n");
getInput(&b2);
printf("\n\n录入完毕,现在开始打印验证...\n\n");
printf("打印第一本书的信息...\n");
printBook(&b1);
putchar('\n');
printf("打印第二本书的信息...\n");
printBook(&b2);
return 0;
}
这次我们传递过去的就是一个指针,而不是整个庞大的结构体。
注意:这里由于传进来的实参是一个指针,所以要使用箭头(->)来访问结构体变量的成员
动态申请结构体
还可以使用malloc函数动态地在堆里面给结构体分配存储空间
#include <stdio.h>
#include <stdlib.h> //增加头文件
int main(void)
{
struct Book *b1, *b2;
b1 = (struct Book *)malloc(sizeof(struct Book)); //获取Book尺寸分配给b1,并强制转化为指向Book结构体的指针
b2 = (struct Book *)malloc(sizeof(struct Book));
if (b1 == NULL || b2 == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
printf("请录入第一本书的信息...\n");
getInput(b1);
putchar('\n');
printf("请录入第二本书的信息...\n");
getInput(b2);
printf("\n\n录入完毕,现在开始打印验证...\n\n");
printf("打印第一本书的信息...\n");
printBook(b1);
putchar('\n');
printf("打印第二本书的信息...\n");
printBook(b2);
free(b1);
free(b2);
return 0;
}
构建图书馆
现在要求大家来构建一个图书馆,然后让用户将书籍的信息都录入到里面。
提示:“图书馆” 其实就是存放 Book 结构体变量的指针数组,每个数组元素存放的是指向一个动态申请的 Book 结构体变量的指针。
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;
char publisher[40];
};
void getInput(struct Book *book);
void printBook(struct Book *book);
void initLibrary(struct Book *library[]);
void printLibrary(struct Book *library[]);
void releaseLibrary(struct Book *library[]);
void getInput(struct Book *book)
{
printf("请输入书名:");
scanf("%s", book->title);
printf("请输入作者:");
scanf("%s", book->author);
printf("请输入售价:");
scanf("%f", &book->price);
printf("请输入出版日期:");
scanf("%d-%d-%d", &book->date.year, &book->date.month, &book->date.day);
printf("请输入出版社:");
scanf("%s", book->publisher);
}
void printBook(struct Book *book)
{
printf("书名:%s\n", book->title);
printf("作者:%s\n", book->author);
printf("售价:%.2f\n", book->price);
printf("出版日期:%d-%d-%d\n", book->date.year, book->date.month, book->date.day);
printf("出版社:%s\n", book->publisher);
}
void initLibrary(struct Book *library[])
{
int i;
for (i = 0; i < MAX_SIZE; i++)
{
library[i] = NULL;
}
}
void printLibrary(struct Book *library[])
{
int i;
for (i = 0; i < MAX_SIZE; i++)
{
if (library[i] != NULL)
{
printBook(library[i]);
putchar('\n');
}
}
}
void releaseLibrary(struct Book *library[])
{
int i;
for (i = 0; i < MAX_SIZE; i++)
{
if (library[i] != NULL)
{
free(library[i]);
}
}
}
int main(void)
{
struct Book *library[MAX_SIZE];
struct Book *ptr = NULL;
int ch, index = 0;
initLibrary(library);
while (1)
{
printf("请问是否需要录入图书信息(Y/N):");
do
{
ch = getchar();
} while (ch != 'Y' && ch != 'N');
if (ch == 'Y')
{
if (index < MAX_SIZE)
{
ptr = (struct Book *)malloc(sizeof(struct Book));
getInput(ptr);
library[index] = ptr;
index++;
putchar('\n');
}
else
{
printf("该图书馆已满,无法录入新数据!\n");
break;
}
}
else
{
break;
}
}
printf("\n\n录入完毕,现在开始打印验证...\n\n");
printLibrary(library);
releaseLibrary(library);
return 0;
}

浙公网安备 33010602011771号