定义结构体struct
一个个体的多个属性,是一系列数据的集合。
struct Student 结构体名//先声明结构体类型
{
int ID;
char nike name;
float height;
int grades[10];
struct Cake;//
}student1,student2;//别忘了最后的分号,定义这个类型的两个变量。
struct Student student3;//另一个定义方式
和函数类似,像一张图纸,所以要在其中存放具体数据。区分结构体类型(Student)和结构体变量(student)。只能对变量进行赋值,存取,运算。结构体内的成员名字可以和外面的一样,不代表同一个对象(和函数那边的好像)。
初始化结构体变量
struct Student student3={12312817,"CAKE",32.342,{99,75}};//一开始就初始化
student3=(struct stu){12312817,"CAKE",32.342,{99,75}};//后来初始化
结构体成员的访问和.运算符
student.ID=12312817;//用来指定结构体内部成员
ID=12312817;//错误,无ID定义
wusong.number--;
scanf("%s %s",&wusong.weapon,name);//各种操作都可以。如果结构体里面的是char[20],不用&
printf("%s",wusong.nickname);
同一结构体里的成员可以整体赋值。student1=student2;
但是这个不能整体输出!
结构体的嵌套
结构体的成员本身可以属于某种结构体类型。
struct Date//第一个结构体
{
int year;
int month;
int day;
};
struct Person//第二个结构体
{
char name[20];
struct Date birthday;第二个结构体里面的结构体。
};
结构体数组定义和使用
一般形式:第一种(声明时):struct 结构体名{成员列表}数组名[数组长度];
第二种(声明后):struct 结构体名 数组名[长度]
初始化:={初值列表}
struct Person
{
char name[20];
int count;
} leader[3] = {{"a", 0}, {"b", 0}, {"b", 0}};
struct Person leader[3]={"a",0,"b",0,"c",0};
学生按照成绩排序,应用结构体数组,排序法,结构体内容的交换。
struct Student {//定义结构体
int id;
int score;
};
int main() {
struct Student stu[5] = {{110, 55}, {111, 56}, {112, 34}, {113, 46}, {114, 64},}, temp;
int i, j, k, n = 5;
for (i = 0; i < n - 1; i++) {
for (j = 0; j < n - 1 - i; j++) {
if (stu[j].score < stu[j + 1].score) {//和之前的排序几乎一样
temp = stu[j];//temp结构体用来当中间交换的结构体
stu[j] = stu[j + 1];
stu[j + 1] = temp;
}
}
}
for (k = 0; k < n; k++) {
printf("%d,%d\n", stu[k].id, stu[k].score);
}
}
结构体和指针
指向结构体的指针变量指向结构体变量,可以引用结构体数组里的元素。指针类型和结构体类型一致struct Sudent *pt
访问方式:
struct Student {
int age;
char name[20];
};
struct Student *p, *pt, stu1 = {5, "sb"}, stu[3] = {{3, "cake"}, {2, "wowoji"}, {8, "jueqi"}};//初始化
p = &stu1; //指针指向结构体
pt = stu; //指针指向结构体数组
stu1.name = (*p).name; //两种表示等价
stu1.name = p->name;
for (pt; pt < stu + 3; pt++) { //指向结构体数组的指针怎么使用
printf("%d,%s\n", pt->age, pt->name);
}
结构体做函数参数
1传递结构体变量,相当于传值
2传递对应指针!很棒的方法。
3传整个结构体变量(少用,因为开销大)
好处:参数列表变短了。代码扩展度高
应用示例:
input函数,接受结构体数组指针,往多个结构体里输入数据和求学生平均成绩。
max函数,接受结构体数组指针,找成绩最高的学生,并返回对应结构体。
print 函数,接受了整个结构体,输出成绩最高学生的信息。
#include <stdio.h>
#include <stdlib.h>
#define N 3//预处理
//用指向结构体变量的指针作为实参
struct Student { //结构体定义
int num;
char name[20];
float score[3];//前三个要录入
float aver;
};
void input(struct Student stu[]);
struct Student max(struct Student stu[]);
void print(struct Student stud);//三个函数
int main() {//调用函数
struct Student stu[N], *p = stu; //指针定义
input(p);
print(max(p));
return 0;
}
void input(struct Student stu[]) {
int i;
printf("输入各学生的:学号,姓名,三门课的成绩:\n");
for (i = 0; i < N; i++) {
scanf("%d %s %f %f %f", &stu[i].num, &stu[i].name, &stu[i].score[0], &stu[i].score[1], &stu[i].score[2]);
stu[i].aver = (stu[i].score[0] + stu[i].score[1] + stu[i].score[2]) / 3.0;
}
}
struct Student max(struct Student stu[]) {
int i, m = 0;
for (i = 0; i < N; i++) {
if (stu[i].aver > stu[m].aver)
m = i;
}
return stu[m];
}
void print(struct Student stud) {
printf("\n成绩最高的学生是:\n");
printf("学号:%d,平均成绩:%f\n", stud.num, stud.aver);
}
链表:实现非连续的储存
是一种数据结构,与数组优劣互补。各节点地址不连续,各节点有两个部分(数据,下一结点的地址)。除此之外,还有头指针(起始地址),表尾(指针为NULL)。
优点: 易删改,只需修改元素间关系就行。
化整为零,不需大段连续空间。
缺点: 访问效率低。
链表与结构体
定义:
struct node{
int num;//数据域
struct node*next;//指针域
}a,b,c;
静态链表链接和输出:
struct node *head,*p;
head=&a;//链接
a.next=&b;
b.next=&c;
c.next=NULL;
p=head;//输出开始,找到链子头
do{
printf("%ld%5.1f\n",p->num,p->score);
p=p->next;//p指向了下一个结点!
}while(p!=NULL);
动态链表的建立(基础版)
pa=(struct Student*)malloc(sizeof(struct Student));
pa->num=10101;pa->score=19;//一个结点的创建,不再依赖于结构体变量的不断声明
pb=(struct Student*)malloc(sizeof(struct Student));
pb->num=10101;pb->score=19;
pc=(struct Student*)malloc(sizeof(struct Student));
pc->num=10101;pc->score=19;//多个结点创建
//链接起来
head=pa;//链接
pa->next=pb;
pb->next=pc;
pc->next=NULL;
复用指针建立链表,动态建立多个结点:
三个指针就能实现多个结点的创建和链接
动态创建链表的函数,返回头指针:
#LEN sizeof(struct Student)
//把这一段当一个创建链表的函数
struct Student *creat(void)
{
struct Student *p1,*p2,*head;//p1指向新结点,p2指向最后一个结点
p1=p2=(struct Student*)malloc(LEN);//开辟第一个结点
scanf("%ld,%f",&p1->num,&p1->score);
head=NULL;
int n=0;
while(p1->num!=0)//学号为0时终止
{n=n+1;
if(n==1)
head=p1;//找到链头
else
p2->next=p1;//让结点链接上去
p2=p1;//p2移上去
p1=(struct Student*)malloc(LEN);//开辟新结点
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL//结束封底
return (head);
}
链表结点的插入操作
一般情况:结点s插入结点p和下一个结点之间:先让s->next=p->next;再让p->next=s;注意顺序,别让后面的结点丢失。
表头插入:法一:s->next=head;head=s;
法二(增加头结点:仅仅存放位置信息,使得所有有效结点都有前驱结点):s->next=L->next;L->next=s;这样与一般情况的结构一致。
其中,需要一个循环来找到结点位置。和一个if else判断插入位置。(无头结点)
//找到插入位置 位置:position,结点指针:head,p
p=head;
while(position>=1&&p->next!=NULL)
{
position--;
p=p->next;
}
创建带有头结点的链表
Node *head = malloc(sizeof(Node));
head->data = 0; // 头结点的数据域通常设置为0
head->next = NULL; // 头结点的指针域指向NULL
带头结点的有链列表的合并(以后写)//暂时理解困难,先理解插入与删除
struct node*mergeNodes(struct node*L1, struct node *L2)
{
struct node*p1=L1,*q1=L1->next,*p2=L2,*q2=L2->next;
while (p2!=NULL)//链表遍历常用方式
{
q2=p2->next;//用q2记下p2在L2中的下一个结点
while (q1!=NULL&&q1->data<p2->data)//为什么这里的是q1!=NULL而不是p1
{
p1=q1;q1=q1->next;
}
//如果已经到达L1表尾,可直接把L2其余部分链接到L1尾部
if(q1==NULL)
break;
p2->next=q1;
p1->next=p2;
p1=p2;
p2=q2;// p2指向L2的下一个结点
}
if(q1==NULL)
{
p1->next=p2;
}
return L1;
}
带头结点的链表中删去结点
struct node*removeNode(struct node*L,int value)//给链表与删除的位置
{
struct node*p,*q;
p=L;
q=L->next;
while(q!=NULL&&q->data!=value)//寻找删除结点中
{
p=q;
q=q->next;
}
if(q!=NULL)//哥们找到了,但是要判断是不是超范围了
{
p->next=q->next;
}
else{printf("the node is not found!\n")};
free(q);
return L;
}
p->next=p->next->next;(简略)
q=p->next;p->next=q->next;free(q);(干净彻底)
共用体union(别用)
同一段内存存放不同类型的变量。定义方式与结构体一样。理解为:“名字,外号,id”选一个来称呼。
共用体与结构体对比(特点)
相似:先定义后引用。系统仅给共用体变量分配空间。、
不同:结构体变量的大小是各成员之和;共用体是最长成员的长度(共享了内存)。
初始化方式不同,因为共用体同一时刻只能有一个变量存放。共用体变量初始化只能初始化第一个成员。共用体赋值后,之前的信息被取代。&max=&max.a=&max.b。
枚举类型enum erate(完善中)
形式上像构造类型,实际上是基本类型。如果一个变量只有几种可能的值,可定义为枚举类型。
可以更加自然地表示非数值计算中的数据:数值表示月份,星期之类的数据。
声明:
enum Weekday{sun,mon,tue,wed,thu,fri,sat};
定义方式与结构体类似。枚举变量只能从枚举类型中列出的枚举元素中取值。每个枚举元素代表一个整数,按顺序为0,1,2.。。。。枚举变量可以用来判断比较。
// 定义枚举类型
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
// 使用枚举类型作为函数参数
void printDay(enum Weekday day) {
switch(day) {
case Monday:
printf("Today is Monday\n");
break;
case Tuesday:
printf("Today is Tuesday\n");
break;
case Wednesday:
printf("Today is Wednesday\n");
break;
case Thursday:
printf("Today is Thursday\n");
break;
case Friday:
printf("Today is Friday\n");
break;
case Saturday:
printf("Today is Saturday\n");
break;
case Sunday:
printf("Today is Sunday\n");
break;
default:
printf("Invalid day\n");
break;
}
}
int main() {
int dayInput;//虽然说可以直接给today赋值整数,但是这样更合理
printf("Enter the day (0-6): ");
scanf("%d", &dayInput);
// 检查输入是否在枚举范围内
if (dayInput < 0 || dayInput > 6) {
printf("Invalid input\n");
return 1;
}
// 将用户输入的整数转换为枚举类型
enum Weekday today = (enum Weekday)dayInput;
// 使用枚举类型作为输出
printf("The value of today is %d\n", today);
// 调用函数,将枚举类型作为参数传递
printDay(today);
return 0;
}