一、结构体的定义和使用
(一)定义:
1.一般格式:
不同数据类型组成组合型数据结构,例如:学生的姓名,年龄,分数,性别
注意:结构体在声明时不会分配内存空间,只有在进行结构体变量时才会分配,结构体声明时不能赋值
2.结构体的声明:
struct student{//struct是关键字,此结构体类型为struct student
char name[50];
int age;
int score;
};//注意分号
3.结构体变量的三种定义:
struct student{
char name[50];
int age;
int score;
};
int main(){
//1.先声明结构体,后定义结构体变量
struct student stu;//定义了一个结构体变量
return 0;
}
//2.在声明类型的同时定义变量
struct Date{
int year;
int month;
int day
}student1,stuent2;//定义了2个结构体变量
//3.不使用类型,直接定义结构体变量
struct {
int id;
char dept[];
}emp1,emp2;
第一种方式:比较灵活,推荐使用;第二种方式:不灵活,不推荐使用,第三种方式:不推荐使用
### 4.结构体的初始化:
//结构体变量只有在定义时才可以初始化
struct student stu = {"zhangsan",20,90};//正确
stu = {"lisi",20,80};//报错,结构体类似于数组,是常量,只有在定义时可以初始化
(二)使用:
1.如果是普通变量,使用.运算符:
struct student{
char name[50];
int age;
int score;
};
int main(){
struct student stu = {"zhangsan",20,90};
//注意name是数组,赋值使用拷贝,如下:
strcpy(stu.name,"lisi");
stu.age = 15;
stu.score = 70;
printf("%s %d %d",stu.name,stu.age,stu.score);
return 0;
}
2.如果是指针变量,使用->运算符:
struct student{
char name[50];
int age;
int score;
};
int main(){
struct student* p;
strcpy(p -> name,"wangwu");
p -> age = 15;
p -> score = 90;
printf("%s %d %d",p->name,p->age,p->score);
return 0;
}
二、结构体数组
(一)定义:
(二)应用:
struct student{
char name[50];
int age;
int score;
};
int main(){
struct student stu[5] = {
{"zhangsan",20,90},
{"lisi",18,90},
{"wangwu",20,90},
{"maqi",20,90},
{"dongdong",17,90},
};
int average;//平均分
int sum = 0; //总成绩
for(int i=0;i<5;i++){
sum = sum + stu[i].score;
}
average = sum / 5;
printf("平均成绩为:%d\n",average);
return 0;
}
(三)结构体的嵌套:
struct person{
char name[50];
char sex;
};
struct student{
int id;
struct person p;//嵌套
};
int main(){
struct student stu;
stu.id = 100;
strcpy(stu.p.name,"lisi");
stu.p.sex = 'M';
printf("%d %s %c\n",stu.id,stu.p.name,stu.p.sex);
return 0;
}
通过指针定义同理stu->p->age,也可以:stu->p.name
三、结构体指针
(一)定义:
(二)指向结构体的指针:
struct student{
char name[50];
int id;
char sex;
int score;
};
int main(){
struct student stu;
struct student* p;
p = &stu;//即:p为指向结构体变量的指针
stu.id = 123;
strcpy(stu.name,"zhangsan");
stu.score = 90;
stu.sex = 'F';
printf("%d %s %d %c\n",stu.id,stu.name,stu.score,stu.sex);//通过结构体访问
printf("%d %s %d %c\n",p->id,p->name,p->score,p->sex);
return 0;
}
(三)指向结构体数组的指针:
struct student{
char name[50];
int id;
char sex;
int score;
};
int main(){
struct student stu[3] = {
{"zhangsan",123,'F',90},//name赋值不需要字符拷贝函数
{"lisi",234,'F',90},
{"wangwu",345,'M',90}
};
struct student* p;
for(p = stu;p < stu + 3;p ++){//p++移动元素 p = stu:指针指向结构体数组
printf("%s %d %c %d\n",p->name,p->id,p->sex,p->score);
}
return 0;
}
(四)结构体普通变量作为函数参数:
struct student{
char name[50];
int age;
};
int main(){
struct student stu = {0};//初始化为0
set_student(stu);
printf("name = %s age = %d\n",stu.name,stu.age);
return 0;
}
//给结构体赋值
void set_student(struct student tem){
strcpy(tem.name,"zhangsan");
tem.age = 20;
}
为什么输出结果不是name = zhangsan age = 20?
分析:
在set_student(stu);中,采用的是值传递,值传递不能修改实参的值,故而如此
修改如下:
struct student{
char name[50];
int age;
};
int main(){
struct student stu = {0};//初始化为0
set_student(&stu);//添加取地址符
printf("name = %s age = %d\n",stu.name,stu.age);
return 0;
}
//给结构体赋值
void set_student(struct student* tem){//上文取地址,则此处形参为指针类型
strcpy(tem->name,"zhangsan");//由.改为->
tem->age = 20;//由.改为->
}
(五)结构体指针变量作为函数参数:
struct student{
char name[50];
int age;
};
int main(){
struct student stu = {0};//初始化为0
struct student* p;
p = &stu;
set_student(p);
printf("name = %s age = %d\n",p->name,p->age);
return 0;
}
//给结构体赋值
void set_student(struct student* tem){
strcpy(tem->name,"zhangsan");
tem->age = 20;//由.改为->
}
(六)使用const修饰结构体指针形参变量:
struct student{
char name[50];
int age;
};
int main(){
struct student stu;
show (&stu);
show2(&stu);
return 0;
}
void show(struct student* const stu){
//stu = NULL;//error 当const修饰时,stu是常量,不能修改
//*stu = NULL;//也是错误
stu->age = 10;//正确
}
void show2(const struct student* stu){
stu = NULL;
//*stu = NULL;//error
//stu->age = 10; //error
}
四、链表
(一)概念:
(二)建立静态链表:
//建立三个学生的链表
struct Node{
int num;
struct Node* next;
};
struct student{
float score;
struct student* next;//下一节点指针
};
int main(){
struct student a,b,c,*head,*p;//*head为头指针,*p为遍历(输出)指针
//将链表首元素a的地址赋值给head
head = &a;
a.score = 89.5;
a.next = &b;//a.next是指针,赋值是地址故需要&
b.score = 90;
b.next = &c;
c.score = 85;
c.next = NULL;
p = head;//输出链表
do{
printf("score = %.1f ",p->score);
p = p->next;
} while(p != NULL);
return 0;
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/8bce98b76bc04f7fbb86e016971d8955.png#pic_center
五、共用体、枚举、typedef
(一)共用体:
1.概念:
2.union的大小是内部最大的成员大小:
union test{
unsigned int a;
unsigned short b;
unsigned long long c;
};
int main(){
printf("union test的大小:%d\n",sizeof(union test));//union test:数据类型,与结构体相似
printf("long long的大小:%d\n",sizeof(long long));
return 0;
}
3.共用体共用同一个内存段,每个成员的地址一样:
union test{
unsigned int a;
unsigned short b;
unsigned long long c;
};
int main(){
union test t;
printf("%p %p %p\n",&t.a,&t.b,&t.c);
return 0;
}
因为每个成员地址一样,给某个成员赋值,会影响到其他成员:
union test{
unsigned int a;
unsigned short b;
unsigned long long c;
};
int main(){
union test t;
t.a = 0x123;
printf("%x %x %x\n",t.a,t.b,t.c);//%x:十六位输出
return 0;
}
(二)枚举:
1.概念:
2.若第一个成员未赋值,则系统默认赋值0,依次递增:
enum weekday{
sun,mon,tue,thu,fri,sat
};
int main(){
enum weekday a,b,c;
a = sun;
b = mon;
c = tue;
printf("a = %d b = %d c = %d\n",a,b,c);
return 0;
}
3.枚举练习:
enum flag{
true,false
};
int main(){
enum flag f;
f = true;
if(f == 1){
printf("真!");
}else{
printf("假!");//输出,因为第一个类型默认为0
}
return 0;
}
(三)typedef:
1.概念:
2.typedef其实就是起别名:
typedef int int64;
int64 a = 10; //等价于int a = 10
printf("%d\n",a);
3.typedef给结构体取别名:
typedef struct student{
int id;
double score;
int num;
}student;//给结构体取的别名
int main(){
student stu;//不再需要struct student stu,用别名代替
return 0;
}