C语言里可以在一个存储区里记录多个相关数字
这种存储区的类型叫结构体,结构体类型需要首先创建出来才能使用
结构体类型存储区里可以包含多个子存储区,每个子存储区可以用来记录一个数字
结构体存储区中子存储区的个数任意,不同子存储区的类型可以不同
子存储区的类型也可以是结构体类型
声明结构体的时候需要使用struct关键字
1#include<stdio.h>
2int main(){
3 struct person{
4 int age;
5 float height;
6 char name[10];
7 };
8 return 0;
9 }
结构体声明语句中变量声明语句没有分配存储区,它们只是表示有多少个子存储区以及每个子存储区的类型
结构体声明中的每个变量叫做结构体的成员变量
C语言里不可以在结构体中包含函数
结构体声明语句可以写在任何地方(包括头文件里),因为它不分配存储区
可以把结构体作为类型声明变量,这种变量叫结构体变量
结构体变量记录了存储区,它可以真正用来记录数字
声明结构体变量的时候需要把struct关键字和结构体名称合并作为类型变量
typedef 关键字可以用来给数据类型起别名,可以替代原有的类型名称
1#include<stdio.h>
2struct person{
3 int age;
4 float height;
5 char name[10];
6};
7typedef struct person sperson;
8int main(){
9 struct person prn;// 结构体变量声明
10 sperson prn1;//结构体变量声明 等同于上条
11 return 0;
12 }
可以把起别名的语句和结构体声明语句合并成一条语句
1#include<stdio.h>
2typedef struct person{
3 int age;
4 float height;
5 char name[10];
6 }sperson;
7int main(){
8 struct person prn;// 结构体变量声明
9 sperson prn1;//结构体变量声明 等同于上条
10 return 0;
11 }
此时也可以省略结构体名称
1#include<stdio.h>
2typedef struct /*person*/{
3 int age;
4 float height;
5 char name[10];
6 }sperson;
结构体变量在声明的时候应该初始化,可以像初始化数组一样初始化结构体变量
通常不能把结构体变量当作整体使用,一次只应该使用其中某个子存储区
可以在结构体变量后使用 . 操作符,然后加上某个成员变量名称,这个写法可以表示成员变量代表的子存储区
1#include<stdio.h>
2typedef struct /*person*/{
3 int age;
4 float height;
5 char name[10];
6 }sperson;
7int main(){
8 sperson prn1={0};
9 printf("请输入年龄");
10 scanf("%d",&(prn1.age));
11 printf("请输入身高");
12 scanf("%g",&(prn1.height));
13 scanf("%*[^\n]");
14 scanf("%*c");
15 printf("请输入姓名");
16 fgets(prn1.name,10,stdin);
17 printf("年龄是%d",prn1.age);
18 printf("身高是%g",prn1.height);
19 printf("姓名是%s",prn1.name);
20 return 0;
21 }
同类型结构体变量之间可以直接赋值
1#include<stdio.h>
2typedef struct /*person*/{
3 int age;
4 float height;
5 char name[10];
6 }sperson;
7int main(){
8 sperson prn1={0},prn2={0};
9 printf("请输入年龄");
10 scanf("%d",&(prn1.age));
11 printf("请输入身高");
12 scanf("%g",&(prn1.height));
13 scanf("%*[^\n]");
14 scanf("%*c");
15 printf("请输入姓名");
16 fgets(prn1.name,10,stdin);
17 printf("年龄是%d",prn1.age);
18 printf("身高是%g",prn1.height);
19 printf("姓名是%s",prn1.name);
20 prn2=prn1;
21 printf("年龄是%d",prn2.age);
22 printf("身高是%g",prn2.height);
23 printf("姓名是%s",prn2.name);
24 return 0;
25 }
结构体指针可以和结构体存储区捆绑(记录结构体存储区的地址)
当结构体指针和结构体存储区捆绑后,可以在结构体指针后使用 -> 操作符再加上成员变量名称,表示成员变量代表的子存储区
int main(){
8 sperson prn1={0},prn2={0};
9 sperson *p_person=NULL;
21 p_person=&prn1;
22 printf("年龄是%d",p_person->age);
23 printf("身高是%g",p_person->height);
24 printf("姓名是%s",p_person->name);
25 printf("\n");
26 printf("年龄是%d",(*p_person).age);
27 printf("身高是%g",(*p_person).height);
28 printf("姓名是%s",(*p_person).name);
练习:画图 声明结构体用来记录屏幕上一个像素的位置
声明结构体用来记录屏幕上一个水平长方形的位置
编写程序从键盘得到两个点的位置,计算中间点的位置,并把结果显示在屏幕上
1 #include<stdio.h>
2 typedef struct {
3 int cow,col;
4 } pt;
5 typedef struct{
6 pt pt1,pt2;
7 }rect;
8 int main(){
9 pt pt1={0},pt2={0},mid={0};
10 printf("请输入点1的位置: ");
11 scanf("%d%d",&(pt1.cow),&(pt1.col));
12 printf("请输入点2的位置: ");
13 scanf("%d%d",&(pt2.cow),&(pt2.col));
14 mid.cow=(pt1.cow+pt2.cow)/2;
15 mid.col=(pt1.col+pt2.col)/2;
16 printf("中间点的位置:%d %d\n",mid.cow,mid.col);
17 }
用指针的改法:
1 #include<stdio.h>
2 typedef struct {
3 int cow,col;
4 } pt;
5 typedef struct{
6 pt pt1,pt2;
7 }rect;
8 int main(){
9 pt pt1={0},pt2={0},mid={0};
10 pt *p_1=NULL,*p_2=NULL,*p_mid=NULL;
11 p_1=&pt1;
12 p_2=&pt2;
13 printf("请输入点1的位置: ");
14 scanf("%d%d",&(p_1->cow),&(p_1->col));
15 printf("请输入点2的位置: ");
16 scanf("%d%d",&(p_2->cow),&(p_2->col));
17 p_mid->cow=(p_1->cow+p_2->cow)/2;
18 p_mid->col=(p_1->col+p_2->col)/2;
19 printf("中间点的位置:%d %d\n",p_mid->cow,p_mid->col);
20 return 0;
21 }
当从调用函数向被调用函数传递结构体数据的时候应该采用结构体指针作为形式参数,这样节省时间和空间
1#include<stdio.h>
2typedef struct{
3 int cow,col;
4}pt;
5void print(pt *p_pt){
6 printf("点的位置是(%d %d)",p_pt->cow,p_pt->col);
7 }
8int main(){
9 pt pt1={0};
10 printf("请输入一个点的位置: ");
11 scanf("%d%d",&(pt1.cow),&(pt1.col));
12 print(&pt1);
13 }
结构体指针形式参数在声明的时候尽量使用const关键字
5void print(const pt *p_pt)
复习const
声明指针变量时可以使用const关键字
声明指针变量时可以把const关键字写在类型名称前。作用是不可以通过这种指针对捆绑的存储区赋值,但是可以对指针本身做赋值。例:
4 int *p_num=#
5 *p_num=10; 可以
1 #include<stdio.h>
2 int main(){
3 int num=0;
4 const int *p_num=#
5 // *p_num=10; 错误
6 p_num=NULL;
7 return 0;
8}
所有用来实现跨函数使用存储区的指针形式参数都尽量采用以上方式加const关键字
(加个记号,告诉调用函数不会改)
也可以在声明变量的时候把const关键字写在指针变量名称前。作用是不可以对这种指针做赋值,但是可以通过这种指针对捆绑的存储区做赋值
1 #include<stdio.h>
2 int main(){
3 int num=0;
4 int * const p_num1=#
5 *p_num1=10;
6 p_num1=NULL; // 错误
7 return 0;
8 }
从被调用函数向调用函数传递结构体数据的时候,应该使用结构体存储区的地址作为返回值,被调用函数应该提供一个结构体指针类型的存储区用来存放返回值,这样可以避免时间和空间的浪费
不可以把局部结构体存储区的地址作为返回值
所以,以上程序在结构体使用函数调用时应改成:
1#include<stdio.h>
2typedef struct{
3 int row,col;
4}pt;
5pt *read(pt *p_pt){
6 pt pt={0};
7 printf("请输入一个点的位置:");
8 scanf("%d%d",&(p_pt->row),&(p_pt->col));
9 return p_pt;
10 }
11void print(const pt *p_pt){
12 printf("点的位置是(%d %d)",p_pt->row,p_pt->col);
13 }
14int main(){
15 pt pt1={0},*p_pt=NULL;
16 p_pt=read(&pt1);
17 print(p_pt);
18 return 0;
19 }
编写函数计算两个点的中点位置,并把结果传递给调用函数
1#include<stdio.h>
2typedef struct{
3 int row,col;
4}pt;
5pt *midpt(const pt *p_pt1,const pt *p_pt2,pt *p_mid){
6 p_mid->row=(p_pt1->row+p_pt2->row)/2;
7 p_mid->col=(p_pt1->col+p_pt2->col)/2;
8 return p_mid;
9
10 }
11int main(){
12 pt pt1={0},pt2={0},mid={0};
13 pt *p_pt=NULL;
14 printf("请输入点一的位置:");
15 scanf("%d%d",&(pt1.row),&(pt1.col));
16 printf("请输入点二的位置:");
17 scanf("%d%d",&(pt2.row),&(pt2.col));
18 p_pt=midpt(&pt1,&pt2,&mid);
19 printf("中间点是%d %d\n",p_pt->row,p_pt->col);
20 return 0;
21 }
一个存储区的地址必须是它自身大小的整数倍,(double类型存储区的地址只需要是4的整数倍),这个规则叫数据对齐
结构体内部的子存储区通常也应该遵守数据对齐的规则
数据对齐可能导致子存储区之间有浪费的字节
结构体存储区的大小必须是它内部占用内存空间最大基本类型存储区大小的整数倍(如果这个子存储区类型是double,则结构体的大小只需要是4的整数倍),这个规则叫做数据补齐。
数据补齐会导致结构体最后多出一些浪费的字节。
所以尽量把占用字节小的放前面,占用字节大的放后面
例:
1#include<stdio.h>
2typedef struct{
3 char ch[2];
4 int num;
5}tmp;
6typedef struct{
7 char ch;
8 int num;
9 char ch1;
10}tmp1;
11int main(){
12 printf("%d\n",sizeof(tmp));
13 printf("%d\n",sizeof(tmp1));
14 } 结果是:8 12
Tmp 为什么是8? 因为一组是最大类型的整数倍,先占2个,空两个,再有整形的4个
Tmp1 先1个,空3个,整形4个,后一个,空3个 。一组是4个
编写程序:
abcde
pqrsf
oxytg
nwzuh
mlkji
1#include<stdio.h>
2typedef struct{
3 int row,col;
4}pos;
5int main(){
6 char map[5][5]={0};
7 char ch=0;
8 int row=0,col=0;
9 poscur={0},tmp={0},delta={0,1};
10 for(ch='a';ch<='z';ch++){
11 map[cur.row][cur.col]=ch;
12 tmp.row=cur.row+delta.row;
13 tmp.col=cur.col+delta.col;
14 if(tmp.row<0||tmp.row>4||tmp.col<0||tmp.col>4||
15 map[tmp.row][tmp.col]!=0){
16 if(delta.row==0&&delta.col==1){
17 delta.row=1;
18 delta.col=0;
19 }
20 else if(delta.row==1&&delta.col==0){
21 delta.row=0;
22 delta.col=-1;
23 }
24 elseif(delta.row==0&&delta.col==-1){
25 delta.row=-1;
26 delta.col=0;
27 }
28 else if(delta.row==-1&&delta.col==0){
29 delta.row=0;
30 delta.col=1;
31 }
32 }
33 tmp.row=cur.row+delta.row;
34 tmp.col=cur.col+delta.col;
35 cur=tmp;
36 }
37 for(row=0;row<=4;row++){
38 for(col=0;col<=4;col++){
39 printf("%c",map[row][col]);
40 }
41 printf("\n");
42 }
43 return 0;
44 }