C语言 结构体重难点突破!
结构体类型
概念及定义
struct student |
|||
age |
name[20] |
id |
phoneNumber |
概念:结构体是将彼此相关、类型不同的数据组合在一起的构造数据类型,它是由一些成员(也称为结构分量)组成的。
在使用某个结构之前,必须先根据需要构造它,即声明。
声明结构类型的一般形式为:
struct 结构体名{
//成员列表
类型名 成员名1;
类型名 成员名2;
……
类型名 成员名n;
};
例如:
struct student{ //此结构体里有两个成员:ID和name
int ID;
char name;
};
说明:
(1) struct 结构体名 组合起来叫结构体类型名 ,它是类型不是变量 。
(2)成员列表里可以是基本数据类型,可以是数组类型,甚至可以是结构体类型 。结构体名和成员名必须符合标识符的规定。
struct student{
int ID;
char 1a; //错误的成员命名方法
};
【编译结果】
[Error] expected identifier or '(' before numeric constant
[错误]期望在数字常量前使用标识符或'('
(3)结构类型中的成员名,可以和程序中的变量名同名,他们代表不同的对象,互不干扰 。
例如:
struct student{ //此结构体里有两个成员:ID和name[20]
int ID;
char name[20];
};
int main()
{
int ID = 10;
struct student stu = { 666 , 'd'};
printf("基本数据类型:ID = %d\n",ID);
printf("结构体类型:stu.ID = %d\n",stu.ID);
}
【运行结果】
基本数据类型:ID = 10
结构体类型:stu.ID = 666
(4)定义结构类型时,不允许将成员的数据类型定义成自身的数据类型,因为系统还不知道要分配多少空间。但可以含有指向自身类型的指针变量。
例如:
struct student{
int ID;
char name;
struct student a; //错误的成员定义方法
};
【编译结果】
[Error] field 'a' has incomplete type
[错误]字段'a'的类型不完整
(5)结构类型定义只是说明了结构类型的构成情况,即告诉系统该类型是由哪些类型的成员构成,各占多少个字节,系统并不分配内存空间,只有用结构类型定义了结构变量之后,系统才会为结构变量分配内存空间。
(6)一个结构体变量被分配的内存大小 = 结构体中所有成员内存之和。
int main()
{
struct student{ //4+20+12+4 = 40字节
int age; //4字节
char name[20]; //20字节
struct birthday{ //3*4 = 12字节
int year;
int month;
int day;
}bir;
int id; //4字节
};
struct student stu1 = {20 , "zhangsan" , {2001,06,19} , 17};
printf("字节数为%d\n",sizeof(stu1)); //运行结果:字节数为40
}
(7)结构体和数组的区别:数组是相同类型数据的集合,结构体是不同类型数据的集合。
(8)这是声明语句,分号不可省略。
结构体的嵌套
嵌套结构体的示意图:
struct student |
|||||
age |
name[20] |
struct birthday |
id |
||
year |
month |
day |
1.先定义(成员的结构类型)子结构体,再定义(主结构类型)父结构体
struct birthday{
int year;
int month;
int day;
};
struct student{
int age;
char name[20];
struct birthday bir; //定义一个struct birthday 结构类型的成员 bir
int id;
};
2.在主结构体类型内部定义成员的结构体类型
struct student{
int age;
char name[20];
struct birthday{
int year;
int month;
int day;
}bir;
int id;
};
错误写法:不可以先定义父结构体,再定义子结构体:
struct student{
int age;
char name[20];
struct birthday bir; //定义一个struct birthday 结构类型的成员 bir
int id;
};
struct birthday{
int year;
int month;
int day;
};
【编译结果】
[Error] field 'bir' has incomplete type
[错误]字段'bir'有不完整的类型
实例:嵌套的结构体定义、赋值、引用
#include<stdio.h>
struct student{
int age;
char name[20];
struct birthday{
int year;
int month;
int day;
}bir;
int id;
};
int main()
{
struct student stu1 = {20 , "zhangsan" , {2001,06,19} , 17};
printf("%d\t%s\t%d-%d-%d\t%d\n",stu1.age , stu1.name , stu1.bir.year , stu1.bir.month , stu1.bir.day , stu1.age);
stu1.bir.year = 2002; //从第一级结构体变量开始逐层引用
// bir.year = 2003; //报错,不可以跳过父级结构体变量
scanf("%d",&stu1.bir.month);
printf("%d\t%s\t%d-%d-%d\t%d\n",stu1.age , stu1.name , stu1.bir.year , stu1.bir.month , stu1.bir.day , stu1.age);
}
【运行结果】
20 zhangsan 2001-6-19 20
7
20 zhangsan 2002-7-19 20
结构体变量
1.结构类型和结构变量是两个不同的概念,类比int类型和int型变量。
例如:
int x = 10; //int是类型 x是变量
struct student stu; //struct student 是类型,stu是变量
2. struct 在C语言中不可省略,C++可省略。
定义结构体变量的方法
方法1.先声明结构体类型,再定义结构体变量
struct student{
int ID;
char name;
};
struct student stu; //此时不在函数内,是全局变量
方法2.同时定义结构体类型与结构体变量
struct student{
int ID;
char name;
}stu; //同时定义
方法3. 不定义结构体类型,只定义结构体变量
struct {
int ID;
char name;
}stu1,stu2;
stu1.ID = 1;
stu2.ID = 2;
printf("stu1.ID = %d\nstu2.ID = %d\n",stu1.ID,stu2.ID);
【运行结果】
stu1.ID = 1
stu2.ID = 2
方法3在定义结构体类型的语句后无法再定义这个类型的其他结构变量了,除非再写一遍,或者前面加typedef起个别名。如果再写一遍,也和原来的结构类型属于不同的结构类型。
例如:
struct {
int ID;
char name;
}stu1;
//上面的结构体和下面的虽然结构一样,但它们是属于不同的结构类型。
struct {
int ID;
char name;
}stu2;
例如:给这个结构体起个别名可再次定义其他结构变量。
#include<stdio.h>
int main()
{
typedef struct {
int ID;
char name;
}stu;
stu stt;
stt.ID = 1;
stt.name = 'a';
stu stb = {2,'b'};
printf("stt.ID = %d\tstt.name = %d\n",stt.ID,stt.name);
printf("stb.ID = %d\tstb.name = %d\n",stb.ID,stb.name);
}
【运行结果】
stt.ID = 1 stt.name = a
stb.ID = 2 stb.name = b
对结构体变量赋值的方法
struct student{
int ID;
char name;
}stu; //同时定义
1.赋值符号逐个赋值
struct student stu;
stu.ID = 666;
stu.name = 'd';
printf("stu.ID = %d\nstu.name = %c\n",stu.ID,stu.name);
2.定义时赋值(类似于数组的赋值方式,一一对应赋值)
int main()
{
struct student stu = {666 , 'd'};
printf("stu.ID = %d\nstu.name = %c\n",stu.ID,stu.name);
}
【运行结果】两种方式运行结果相同,均为:
stu.ID = 666
stu.name = d
定义时赋值也可在定义结构体类型同时定义结构体变量同时赋值
struct student{
int ID;
char name;
}stu1 = {777,'e'}; //定义结构体类型同时定义结构体变量同时赋值
int main()
{
struct student stu = {666 , 'd'};
printf("stu.ID = %d\nstu.name = %c\n",stu.ID,stu.name);
printf("stu1.ID = %d\nstu1.name = %c\n",stu1.ID,stu1.name);
}
【运行结果】
stu.ID = 666
stu.name = d
stu1.ID = 777
stu1.name = e
题型:输入两个学生的学号、姓名、成绩,输出成绩较高的学生的学号、姓名、成绩
结构变量的引用
格式:结构变量名.成员名
同类型的结构体变量可以相互整体赋值,但不能进行关系运算
如:
#include<stdio.h>
struct student{
int ID;
char name;
}stu1 = {777,'e'}; //定义结构体类型同时定义结构体变量同时赋值
int main()
{
struct student stu = {666 , 'd'};
printf("stu.ID = %d\tstu.name = %c\n",stu.ID,stu.name);
printf("stu1.ID = %d\tstu1.name = %c\n",stu1.ID,stu1.name);
stu1 = stu;
printf("stu1.ID = %d\tstu1.name = %c\n",stu1.ID,stu1.name);
}
【运行结果】
stu.ID = 666 stu.name = d
stu1.ID = 777 stu1.name = e
stu1.ID = 666 stu1.name = d
typedef关键字
typedef关键字放在struct结构体名之前,作用是:为此结构体类型定义一个别名。
例如:
结构体名前加上typedef,那么这里的stu就不代表结构体变量,而是结构体类型struct student类型的一个别名。
typedef struct student{
int ID;
char name;
}stu; //这里的stu不是结构体变量,而是struct student类型的别名
如果在主函数内不定义结构体变量,直接使用stu.ID,会报错(因为结构体变量还不存在)。
例如:在上面语句的基础上,使用stu.ID、stu.name:
stu.ID = 666;
stu.name = 'd';
【编译结果】
[Error] expected identifier or '(' before '.' token
[错误]期望的标识符或'(',在'.'之前 token:令牌
正确的使用结构体变量方法:
1.定义结构体变量
2.使用结构体变量
例如:下面代码 8~12行
typedef struct student{
int ID;
char name;
}stu;
int main()
{
//1.定义结构体变量
stu stt; //或struct student stt;
//2.使用结构体变量
stt.ID = 666;
stt.name = 'd';
printf("stt.ID = %d\nstt.name = %c\n",stt.ID,stt.name);
}
【运行结果】
stt.ID = 666
stt.name = d
typedef struct student{
int age;
char name[20];
int id;
char phoneNumber[20];
}stu1,stu2; //此时,stu1,stu2都代表着结构类型名,可以任意使用
结构体数组
概念:存放结构体类型数据的数组就叫结构体数组
形式:
struct student{
int ID;
char name;
}stu[10]; //表示stu数组里有10个结构体类型的元素(stu[0] ~ stu[9]),
//每个元素都有两个成员(ID、name)
stu[0].ID = 666;
stu[0].name = 'd';
stu[1].ID = 777;
stu[1].name = 'e';
printf("stu[0]的ID是:%d,name是:%c\n",stu[0].ID,stu[0].name);
printf("stu[1]的ID是:%d,name是:%c\n",stu[1].ID,stu[1].name);
【运行结果】
stu[0]的ID是:666,name是:d
stu[1]的ID是:777,name是:e
拓展: 为什么结构体数组元素下标越界了还可正常输出?
https://ask.csdn.net/questions/7469356
定义结构体数组的方法(3)
同结构体元素变量类似
1.先定义结构体类型,后定义结构体数组
struct student
{
int age;
char name[20];
int id;
char phoneNumber[20];
}; //1.定义结构体类型
struct student stu1[5]; //2.定义结构体数组
2.定义结构体类型的同时定义结构体数组
struct student
{
int age;
char name[20];
int id;
char phoneNumber[20];
}stu2[5],stu3[5]; //同时定义结构体类型和结构体数组
3.不定义结构体类型,只定义结构体树组
struct
{
int age;
char name[20];
int id;
char phoneNumber[20];
}stu2[5],stu3[5];
对结构体数组元素赋值的方法
注意
在赋值时,下面这组代码,定义的数组长度为5,但置存放了三个元素的数据,那么剩余两个元素的数据:int型将为0 ,字符串型将为:空字符串
|
stu3[0] |
stu3[2] |
stu3[3] |
stu3[4] |
stu3[5] |
age |
21 |
22 |
23 |
0 |
0 |
name[20] |
小红 |
小力 |
小强 |
|
|
id |
21 |
22 |
23 |
0 |
0 |
phoneNumber[20] |
13423232323 |
186932455323 |
131245709431 |
|
|
一、全部元素初始化
1.定义结构体数组同时给数组元素初始化
(1)定义结构体类型的同时定义结构体数组同时对数组元素整体初始化
struct student
{
int age;
char name[20];
int id;
char phoneNumber[20];
}stu3[5] = { {21,"小红",21,"13423232323"} , {22,"小力",22,"186932455323"} , {23,"小强",23,"131245709431"} };
printf("\n%d\n",sizeof(stu3)); //48 ×5 = 240
printf("\n=============定义结构体类型的同时定义结构体数组同时给数组元素初始化=\n");
for(i=0;i<5;i++)
{ printf("%d\t%s\t%d\t%s\t\n",stu3[i].age,stu3[i].name,stu3[i].id,stu3[i].phoneNumber);
}
=============定义结构体类型的同时定义结构体数组同时给数组元素初始化=
21 小红 21 13423232323
22 小力 22 186932455323
23 小强 23 131245709431
0 0
0 0
(2)先定义结构体类型,后定义结构体数组的同时对数组元素整体初始化(B站老师推荐)
struct student
{
int age;
char name[20];
int id;
char phoneNumber[20];
}
struct student stu1[5] = { {17,"张三",17,"13111111111"} , {18,"李四",18,"13000000000"} };
for(i=0; i <2; i++) printf("%d\t%s\t%d\t%s\t\n",stu1[i].age,stu1[i].name,stu1[i].id,stu1[i].phoneNumber);
===============定义结构体数组同时给数组元素初始化=================
17 张三 17 13111111111
18 李四 18 13000000000
注意:这种写法可写为
2.先定义结构体类型,后定义结构体数组,再通过for循环scanf给数组元素赋值
struct student stu2[2];
for(i=0; i<2; i++)
{
printf("请输入第%d个学生的信息:",i+1);
scanf("%d%s%d%s",&stu2[i].age,stu2[i].name,&stu2[i].id,stu2[i].phoneNumber);
}
for(i=0; i<2; i++)
printf("%d\t%s\t%d\t%s\n",stu2[i].age,stu2[i].name,stu2[i].id,stu2[i].phoneNumber);
===============先定义结构体数组,后通过for循环scanf赋值 ===========
请输入第1个学生的信息:16 王五 16 13230202939
请输入第2个学生的信息:15 赵六 15 18597059486
16 王五 16 13230202939
15 赵六 15 18597059486
二、部分元素初始化
请查找上面的例子。
三、初始化形式
1.大括号里有小括号
struct student stu1[] = { {17,"张三",17,"13111111111"} , {18,"李四",18,"13000000000"} };
2.一个大括号里依次写全部元素
struct student stu1[] = { 17,"张三",17,"13111111111", 18 ,"李四",18,"13000000000"};
3.可以只给部分结构体数组元素赋值,但结构体数组元素中的每个成员都要初始化。
如下:结构体数组长度为5,但是
struct student stu1[5] = { 17,"张三",17,"13111111111", 18,"李四",18,"13000000000"};
不可以省略某个成员,如下是错误的:
也不可以跳过某个数组元素(例如我想跳过stu[1]这个数组元素,给stu1[0]和stu[2]赋值,这是C语言不允许的(可以通过scanf分别给stu[0]和stu[2]赋值)),
如下面代码会报错:
struct student stu1[5] = {
{17,"张三",17,"13111111111"}, //stu[0]
{ , , , } , //stu[1] 想通过初始化跳过stu[1]是不可能的
18,"李四",18,"13000000000" //stu[2]
};
可通过scanf分别给stu[0]和stu[2]赋值:
int main()
{
int i;
printf("年龄\t姓名\t学号\t手机\n");
struct student stu1[5];
scanf("%d%s%d%s",&stu1[0].age,stu1[0].name,&stu1[0].id,stu1[0].phoneNumber);
scanf("%d%s%d%s",&stu1[2].age,stu1[2].name,&stu1[2].id,stu1[2].phoneNumber);
printf("\n输出结构体数组每个元素的信息:\n");
for(i=0; i<5; i++)
{ printf("%d\t%s\t%d\t%s\t\n",stu1[i].age,stu1[i].name,stu1[i].id,stu1[i].phoneNumber);
}
}
年龄 姓名 学号 手机
17 张三 16 1312993322
16 李四 20 1313131131
输出结构体数组每个元素的信息:
17 张三 16 1312993322
11342688 268501009
16 李四 20 1313131131
8 73
-1 I 4203945
结构体数组的引用
形式 |
实例 |
1.结构数组名[下标].成员名 |
stu[0].name |
2.( * (结构数组名 + 下标) ).成员名 (必须加括号) |
( * (stu + 0) ) . name |
3.(结构数组名+下标) -> 成员 |
(stu + 0) -> name |
经测试,( * (stu + 0) ) . name 和 (stu + 0) -> name 的 + 0 均可省略(因为stu是数组名,和stu+0 一样,都代表数组首元素的地址)
例1.结构体数组的引用输出
//结构体数组的引用
typedef struct student
{
int age;
char name[20];
int id;
char phoneNumber[20];
}stt; //类型名
#include<stdio.h>
int main()
{
int i;
stt stu[5] = {
{16,"张三",16,"5842064"},
{17,"李四",17,"5842065"},
};
printf("年龄\t姓名\t学号\t电话\n");
printf("--------------------------------\n");
printf("\n形式1.使用 结构数组名[].成员 引用并输出:\n");
for (i = 0; i < 2; i++)
{
printf("%d\t%s\t%d\t%s\n", stu[i].age, stu[i].name, stu[i].id, stu[i].phoneNumber);
}
printf("\n形式2.使用 (*(结构数组名+)).成员 引用并输出:\n");
for (i = 0; i < 2; i++)
{
printf("%d\t%s\t%d\t%s\n", (*(stu + i)).age, (*(stu + i)).name, (*(stu + i)).id, (*(stu + i)).phoneNumber);
}
printf("\n形式3.使用 数组名[].成员 引用并输出:\n");
for (i = 0; i < 2; i++)
{
printf("%d\t%s\t%d\t%s\n", (stu + i)->age, (stu + i)->name, (stu + i)->id, (stu + i)->phoneNumber);
}
return 0;
}
年龄 姓名 学号 电话
--------------------------------
形式1.使用 结构数组名[].成员 引用并输出:
16 张三 16 5842064
17 李四 17 5842065
形式2.使用 (*(结构数组名+)).成员 引用并输出:
16 张三 16 5842064
17 李四 17 5842065
形式3.使用 数组名[].成员 引用并输出:
16 张三 16 5842064
17 李四 17 5842065
例2.结构体数组的引用修改
//结构体数组的引用修改
typedef struct student
{
int age;
char name[20];
int id;
char phoneNumber[20];
}stt; //类型名
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
stt stu[5] = {
{16,"张三",16,"5842064"},
{17,"李四",17,"5842065"},
};
printf("\n形式1.使用 结构数组名[下标].成员 引用-修改年龄、姓名并输出:\n");
stu[0].age = 100; //修改年龄
strcpy(stu[0].name,"勇敢牛牛"); //修改姓名
printf("%d\t%s\n", stu[i].age , stu[i].name);
printf("\n形式2.使用 (*(结构数组名 + 下标)).成员 引用-修改年龄、姓名并输出:\n");
(*(stu+0)).age = 200; //修改年龄
strcpy( ( *(stu + 0) ) .name , "不怕困难"); //修改姓名
printf("%d\t%s\n", (*(stu + i)).age, (*(stu + i)).name);
printf("\n形式3.使用 (结构数组名+下标) -> 成员 引用-修改年龄、电话并输出:\n");
(stu + 0) -> age = 300; //修改年龄
strcpy((stu + 0) -> phoneNumber,"9999999");//修改 stu[0]的电话
printf("%d\t%s\n", (stu + i)->age, (stu + i)->phoneNumber);
return 0;
}
【运行结果】
形式1.使用 结构数组名[下标].成员 引用-修改年龄、姓名并输出:
100 勇敢牛牛
形式2.使用 (*(结构数组名 + 下标)).成员 引用-修改年龄、姓名并输出:
200 不怕困难
形式3.使用 (结构数组名+下标) -> 成员 引用-修改年龄、电话并输出:
300 9999999
结构体数组的字节数和有效长度
如下列代码,可得知:
定义结构体数组时,字节数为结构体的数组长度(可以省略结构体数组的长度,此时系统会根据初始化时 小数组的个数确定数组长度) × 结构体类型字节数(各成员字节数之和)
struct student //共48字节
{
int age; //4字节
char name[20]; //20字节
int id; //4字节
char phoneNumber[20]; //20字节
}stu3[5] = { {21,"小红",21,"13423232323"} , {22,"小力",22,"186932455323"} , {23,"小强",23,"131245709431"} }; //定义了长度
printf("\n%d\n",sizeof(stu3)); //48 × 5 == 240
struct student stu1[] = { {17,"张三",17,"13111111111"} , {18,"李四",18,"13000000000"} }; //没有定义长度
printf("%d\n",sizeof(stu1)); // 48 × 2 == 96
结构体指针
指针可以指向任何类型:基本数据类型、数组、结构体类型……
结构指针:指向结构变量的指针。
结构指针变量的值是 结构指针指向的结构变量占用内存空间的起始地址。
通过结构指针访问结构体变量的成员,与直接使用结构变量的效果一样。
结构指针的定义
结构类型名 *结构指针变量名;
例如:
struct student *p;
结构体指针的初始化
结构类型名 *结构指针变量名 = &结构变量名
例如:
struct student *p = &stu;
结构体指针的引用
使用结构指针访问结构变量的成员(三种形式)
形式 |
实例 |
1.结构变量名.成员名 |
stu.ID |
2.(*结构指针).成员名 (必须加括号) |
(*p).ID |
3.结构指针 -> 成员名 |
p->ID |
读法(对应上表): ( . 翻译成 的 ,->翻译成 指向 )
结构体变量的成员 |
stu结构体变量的ID成员 |
结构指针指向的结构变量的成员 |
p结构体指针指向的stu结构变量的成员名 |
结构体指针指向的成员 |
p结构体指针指向的ID成员 |
说明:
1.三种形式等价
2.形式2要注意:结构成员运算符(.)比取指针内容(*)的优先级高,所以必须加在*p外加(),否则会报错。
例如:
printf("(*p).ID = %d\t(*p).name = %c\n",*p.ID,(*p).name);
【编译结果】
[Error] request for member 'ID' in something not a structure or union
[错误]成员'ID'不是一个结构或联合
应用实例:使用三种形式访问结构变量的成员
stu.ID = 666;
stu.name = 'd';
//结构变量名.成员名
printf("stu.ID = %d\tstu.name = %c\n",stu.ID,stu.name);
//定义一个指针(p)并初始化(指向结构体变量stu)
struct student *p = &stu;
//(*结构指针变量名).成员名
printf("(*p).ID = %d\t(*p).name = %c\n",(*p).ID,(*p).name);
//结构指针变量名 -> 成员名
printf("p->ID = %d\tp->name = %c\n",p->ID,p->name);
//通过结构指针更改成员值
p->ID = 777;
p->name = 'e';
printf("\n使用结构指针更改成员值:\nstu.ID = %d\tstu.name = %c\n",p->ID,p->name);
【运行结果】
stu.ID = 666 stu.name = d
(*p).ID = 666 (*p).name = d
p->ID = 666 p->name = d
使用结构指针更改成员值:
stu.ID = 777 stu.name = e
例2:同时定义结构体类型和结构体指针
int main()
{
struct student{
int age;
char name[20];
int id;
char phoneNumber[20];
}stu1,stu2,*p; //1.同时定义结构体类型和结构体指针
printf("年龄\t姓名\t学号\t电话\n");
printf("========================================\n");
struct student stu3 = {18 , "小三" , 2 , "13111112313" };
p = &stu3;
//三种方式修改成员值 printf("%d\t%s\t%d\t%s\n",stu3.age,stu3.name,stu3.id,stu3.phoneNumber);
printf("%d\t%s\t%d\t%s\n",p->age,p->name,p->id,p->phoneNumber);
printf("%d\t%s\t%d\t%s\n",(*p).age,(*p).name,(*p).id,(*p).phoneNumber);
//->方式修改成员值
p->age = 11;
strcpy(p->name,"zhangsan");
printf("%d\t%s\t%d\t%s\n",(*p).age,(*p).name,(*p).id,(*p).phoneNumber);
//(*p).方式修改成员值
(*p).age = 12;
strcpy((*p).name,"lisi");
printf("%d\t%s\t%d\t%s\n",(*p).age,(*p).name,(*p).id,(*p).phoneNumber);
}
【运行结果】
年龄 姓名 学号 电话
========================================
18 小三 2 13111112313
18 小三 2 13111112313
18 小三 2 13111112313
11 zhangsan 2 13111112313
12 lisi 2 13111112313
- 点赞
- 收藏
- 关注作者
评论(0)