结构体
概述:
-
问题定义:有时候需要将不同类型的数据组合成一个有机的整体,以方便引用。
-
结构体变量的定义,引用;
-
先声明结构体类型在定义变量
struct student student1,student2; 定义了student1和student2为struct student类型的变量,即它们具有struct student类型的变 量,即它们具有struct student类型的结构。
结构体变量的引用应遵守以下规则
1、不能将一个结构体变量作为一个整体进行输入和输出。
2、student.num表示student变量中的num成员。”.“是成员分量运算符,它在所有的运算符中优先级最高,因此可以把student.num作为一个整体来看待。
#include<stdio.h> int main(void){ struct student{ int num; char *name; char sex; float score; }boy1,boy2; boy1.num=7; boy1.name="syy"; printf("Please enter sex and score:\n"); scanf("%c%f",&boy1.sex,&boy1.score); boy2=boy1; printf("Number=%d\nName=%s\nSex=%c\nScore=%f\n",boy2.num,boy2.name,boy2.sex,boy2.score); }
3、对结构体变量的成员可以像普通变量成员的地址,也可以像普通变量一样进行运算
4、可以引用结构体变量成员的地址,也可以引用结构体变量的地址;结构体变量的地址主要用于作函数参数,传递结构体变量的地址。
-
-
结构体变量的初始化;
-
初始化的代码片段:
#include<stdio.h> int main(void){ struct student{ int num; char *name; char sex; float score; }boy1,boy2={102,"syy",'M',98.9}; boy1=boy2; printf("Number=%d\nName=%s\nSex=%c\nScore=%f\n",boy2.num,boy2.name,boy2.sex,boy2.score); }
-
定义结构体数组
-
和定义结构体变量的方法相似,只需要说明其为数组即可。
-
struct student{
····· };struct student student【3】;
-
struct student{
····· } student【3】;
-
与其他类型的数组一样,对结构体数组可以初始化。
-
struct student{
····· } student【2】={ {··················}, {··················} };
-
先声明结构体的类型,然后定义数组为该结构体类型,在定义数组时初始化。
-
-
指向结构体类型数据的指针
- 可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
- 指针变量也可以用来指向结构体数组中的元素。
- 结构指针的说明的一般形式为:struct 结构名 *结构指针变量名;
- 结构指针必须要先赋值才能使用。
- 赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
- 结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间;只有当某个变量被说明为这种类型的结构时,才对该变量的分配存储空间。因此不存在取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。
- 访问的一般形式为:(*结构指针变量).成员名或为结构指针变量→成员名
- 结构指针变量作为函数参数
- 将一个结构体变量的值传递给另一个函数,有3个方法;
-
用结构体变量作参数
#include<stdio.h> #include<string.h> struct student{ int num; char name[20]; float score[3]; }; void print(struct student);//结构体作为参数 int main(void){ struct student stu; stu.num=8; strcpy(stu.name,"BambooSun");//strcpy把含有'\0'结束符的字符串复制到另一个地址空间,返回值的类型为char*。 stu.score[0]=98.9; stu.score[1]=88.9; stu.score[2]=78.9; print(stu);//结构体变量作为实参传递给print函数 } void print(struct student stu){ printf("\tnum : %d\n",stu.num); printf("\tname : %s\n",stu.name); printf("\tscore_1 : %5.2f\n",stu.score[0]); printf("\tscore_2 : %5.2f\n",stu.score[1]); printf("\tscore_3 : %5.2f\n",stu.score[2]); printf("\n"); }
-
用结构体变量的成员作实参
-
用指向结构体变量(或数组)的指针作为实参,将结构体变量(或数组)的地址传给形参。
#include<stdio.h> #include<string.h> struct student{ int num; char name[20]; float score[3]; }; void print(struct student *);//结构体的指针作为参数 int main(void){ struct student stu; stu.num=8; strcpy(stu.name,"BambooSun");//strcpy把含有'\0'结束符的字符串复制到另一个地址空间,返回值的类型为char*。 stu.score[0]=98.9; stu.score[1]=88.9; stu.score[2]=78.9; print(&stu);//结构体变量的地址作为实参传递给print函数 } void print(struct student *p){//*p=&stu;p指向stu的首地址 printf("\tnum : %d\n",(*p).num); printf("\tname : %s\n",p->name); printf("\tscore_1 : %5.2f\n",p->score[0]); printf("\tscore_2 : %5.2f\n",p->score[1]); printf("\tscore_3 : %5.2f\n",p->score[2]); printf("\n"); }
-
- 将一个结构体变量的值传递给另一个函数,有3个方法;
-
动态存储分配
数组的长度是预先定义好的,在整个过程中固定不变。c语言中不允许动态数组类型。eg:int a【n】;错误;用变量表示长度,想对数组大小作动态说明,这是错误的。但是在实际的编程中,往往会发生这种情况,即所需要的内存空间取决于实际输入的数据,而无法预先确定。
所以为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效的利用内存资源提供了手段。
- 常用的内存管理函数有以下三个:
- 分配内存空间函数malloc、calloc.
- 释放内存空间的函数free
- malloc函数
- 函数原型为void *malloc(unsigned int size);
- 作用是在内存的动态分配存储区中分配一个长度为size的连续存储空间(size是一个无符号数)。
- 此函数的返回值是一个指向分配域起始地址的指针(类型为void)
- 如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)
- calloc函数
- 函数原型为 void *calloc(unsigned n,unsigned size);
- 其作用是在内存的动态存储区中分配n个长度为size的连续存储空间。
- 函数的返回一个指向分配域起始地址的指针;
- 如果分配不成功则返回NULL;
- 用calloc函数可以为一维数组开辟动态存储空间,n为数组元素的个数,每个元素长度为size。
- free函数
- 函数原型为void free(void *p);
- 其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用;
- p是最近一次调用calloc或者malloc函数时返回的值。
- 链表:
-
定义:链表是一种常用的数据结构,是动态地进行存储分配的一种结构。
-
组成:头指针和结点。头指针:存放一个地址,该地址指向第一个元素;结点:用户需要的实际数据和链接结点的指针。
-
把数据放入链表并且输出的代码:
#include<stdio.h> struct student{ long num;//学号 float score;//分数 struct student *next;//指向下一个这样结构的地址 }; int main(void){ struct student a,b,c,*head;//声明a,b,c,*head的指针 均有student一样的结构 a.num=2017204452; a.score=99.0; b.num=2017204435; b.score=70; c.num=2017204470; c.score=95; head=&a;//head指针指向a的地址 a.next=&b; b.next=&c; c.next=NULL; do{ printf("%ld %5.1f\n",(*head).num,head->score); head=head->next; }while(head!=NULL); }
-
所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点的数据和输入各个结点数据,并建立起前后相链的关系。
-
链表创建输出学生的学号和分数的代码:
#include<stdio.h> #include<malloc.h> #include<stdlib.h> #define LEN sizeof(struct student)//student结构的大小 //声明函数,更好看,给程序员看的 struct student *creat();//创建链表 void print(struct student *head);//打印链表 //结构体 struct student{ int num; float score; struct student *next; }; int n;//全局变量用来记录存放了多少数据 int main(void){ struct student *stu; stu=creat();//creat返回值是head,头指针 print(stu);//传入头结点,头指针 printf("\n\n"); system("pause"); } //创建链表 struct student *creat(){ struct student *head,*p1,*p2;//创建了一个指针指向这么一个结构,仅仅开辟了4个字节来存放指针地址 p1=p2=(struct student*)malloc(LEN);//LEN是student结构的大小,p1p2均指向内存分配的这么大小的空间 printf("Please enter the num: "); scanf("%d",&p1->num);//输入的都暂时放在p1指针指向的地址中 printf("Please enter the score: "); scanf("%f",&p1->score); //初始化 head=NULL; n=0;//结点数目 while(p1->num){ n++; if(n==1){ head=p1; } else{ p2->next=p1;//p1后移,指向一个新的结点 } p2=p1;//p2也后移 p1=(struct student *)malloc(LEN);//p1指向一个新的结点空间 printf("Please enter the num: "); scanf("%d",&p1->num);//输入的都暂时放在p1指针指向的地址中 ;若输入的是0则退出循环 printf("Please enter the score: "); scanf("%f",&p1->score); } p2->next=NULL; return head;//head是头结点,指向链表的首地址,因此找到首地址就能知道链表的所有地址 } void print(struct student *head){ struct student *p; p=head; printf("\n\nthere are %d records of the students!\n\n",n); if(head){ while(p){ printf("学生的学号是%d and 成绩是%f\n\n",p->num,p->score); p=p->next; } } }
-
链表的删除操作的代码
#include<stdio.h> #include<malloc.h>//动态存储分配函数头文件,当对内存区进行分配时,调用此函数 #include<stdlib.h>//定义了一些变量类型,工具函数,宏 #define LEN sizeof(struct student) //结构体 struct student{ int num; float score; struct student *next; }; //创建链表 struct student *creat(); //删除链表 struct student *del(struct student *head,int num); //打印链表 void print(struct student *head); int n;//全局变量,结点的数量 ,用来存放多少数据 int main(void){ struct student *stu,*p; int num;//删除的数字 stu=creat();//返回头结点的指针 p=stu; print(p);//打印原来的链表 printf("Please enter the number you want to delete: "); scanf("%d",&num); print(del(p,num));//打印删除后的链表,要求delete的返回值也需要是指针类型的 printf("\n\n"); system("pause"); } //创建新的链表,返回指针的函数 struct student *creat(){ struct student *head,*p1,*p2; p1=p2=(struct student *)malloc(LEN); printf("Please enter the number of the student: "); scanf("%d",&p1->num); printf("Please enter the score of the student: "); scanf("%f",&p1->score); head=NULL; n=0; while(p1->num){ n++; if(n==1){ head=p1; }else{ p2->next=p1; } p2=p1; p1=(struct student *)malloc(LEN); printf("Please enter the number of the student: "); scanf("%d",&p1->num); printf("Please enter the score of the student: "); scanf("%f",&p1->score); } p2->next=NULL; return head; } //打印链表 void print(struct student *head){ struct student *p; p=head; printf("\n\nthere are %d records of the students\n\n ",n); if(head){ while(p){ printf("学生的学号是 %d;学生的成绩是%f\n\n ",p->num,p->score); p=p->next; } } } //删除链表(参数包括要删除的链表以及指定的数据元素值 struct student *del(struct student *head,int num){ struct student *p1,*p2; p1=head; //1头结点为kong if(head==NULL){ printf("This is a empty list!"); } //2数据和当前结点的值不相等 while(p1->num!=num&&p1->next!=NULL){ p2=p1; p1=p1->next; } //数据与当前结点相同 ,删除! if(p1->num==num){ if(p1==head){//删除结点是头结点的时候 head=p1->next; }else{ p2->next=p1->next; } printf("Delete %d succeed!\n",num); n=n-1; }else{ printf("%d not been found!\n",num); } return head; }
-
链表的插入操作的代码
#include<stdio.h> #include<malloc.h> #include<stdlib.h> #define LEN sizeof(struct student) struct student *creat(); struct student *del(struct student *head,int num);//num是要删除的结点 struct student *insert(struct student *head,struct student *stu_2);//1需要被插入的链表,2参数需要插入的地址 void print(struct student *head);//传入首地址头结点 int n;//全局变量,存放学生的个数 struct student{ int num; float score; struct student *next; }; int main(void){ struct student *stu,*p,stu_2,*p1; int num;//学号 stu=creat(); print(stu);//一开始的 printf("Please input the number to delete: "); scanf("%d",&num); printf("\n\n"); p=del(stu,num); print(p);//删除 printf("Please input the number to insert: "); scanf("%d",&stu_2.num);1 printf("Please input the score to insert: "); scanf("%f",&stu_2.score); p1=insert(p,&stu_2); print(p1); printf("\n\n"); system("pause"); } struct student *creat(){ struct student *head,*p1,*p2; p1=p2=(struct student *)malloc(LEN); printf("Please enter the number of the student: "); scanf("%d",&p1->num); printf("Please enter the score of the student: "); scanf("%f",&p1->score); printf("\n\n"); head=NULL; n=0; while(p1->num){ n=n+1; if(n==1){ head=p1; }else{ p2->next=p1; } p2=p1; p1=(struct student*)malloc(LEN); printf("Please enter the number of the student: "); scanf("%d",&p1->num); printf("Please enter the score of the student: "); scanf("%f",&p1->score); printf("\n\n"); } p2->next=NULL; return head; } struct student *del(struct student *head,int num){ struct student *p,*p1; p=head; if(head==NULL){ printf("NULL"); } while(p->num!=num&&p!=NULL){ p1=p; p=p->next; } if(p->num==num){ if(p==head){ head=p->next; }else{ p1->next=p->next; } printf("The %d is delete succeed!",num); n=n-1; }else{ printf("The %d does not be found!",num); } return head; } void print(struct student *head){ struct student *p1,*p2; p1=head; printf("\n\nthere are %d students.\n\n",n); if (head){//时刻想着空链表 while(p1){ printf(" the num is %d: ",p1->num); printf(" the score is %f: \n\n ",p1->score); p1=p1->next; //不要忘记 } } } struct student *insert(struct student *head,struct student *stu_2){ struct student *p0,*p1,*p2; p1=head; p0=stu_2; if(head==NULL){//空链表直接插入,不要忘记处理后置结点 head=p0; head->next=NULL; }else{ while((p0->num>p1->num)&&(p1->next!=NULL)){//目的是找不是结尾的结点,插入在中间的位置 p2=p1; p1=p1->next; } if(p0->num<=p1->num){ if(p1==head){ head=p0;//插入到第一个位置 }else{//如果不是头结点则一定经过上述while循环,则p2->next=p1 p2->next=p0; } p0->next=p1; }else{//也就是p0大于p1且p1->next为空 p1->next=p0;//插入到最后一个位置 } } n=n+1; return head; }
-
- 常用的内存管理函数有以下三个:
-
-
共用体
typedef声明新的类型名来代替已有的类型名
#define PI 3.14(直接替换,比较机械)
-
几种声明类型:
-
声明结构类型:
#include<stdio.h> typedef struct{ int month; int day; int year; }DATE;//为这个结构体定义一个别名 int main(void){ DATE date_one;//结构定义一个变量 date_one.month=12; date_one.year=2021; date_one.day=11; printf("%d-%d-%d\n",date_one.year,date_one.month,date_one.day); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xH8T6sWs-1618905730750)(%E7%BB%93%E6%9E%84%E4%BD%93%20a70db644c66e46e288038e0f752b25d0/Untitled.png)]
-
声明为整形数组:
#include<stdio.h> typedef int num[100];//100有多少个成员而已 int main(void){ num NUM={0};//需要赋初值,不然存在安全隐患 printf("%d\n\n",sizeof(NUM));//占内存大小是400 }
-
声明string为字符指针型
#include<stdio.h> typedef char* p;//别名p int main(void){ p p1;//别名定义一个p1字符 p1="I love SYY!"; printf("%s\n\n",p1); }
-
声明POINTER为指向函数的指针类型,该函数返回整型值
#include<stdio.h> typedef void(*POINTER)();//返回一个void void fun(); int main(void){ POINTER p1;//声明了一个指针类型void(*p1)(); p1=fun;//指向函数的一个指针 p1(); //调用指针指向的函数 } void fun(){ printf("I love SYY!"); }
-
-
用typedef可以声明各种类型名,但不能用来定义变量。
-
用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。
-
当不同源文件中用到同一类型数据时,常用typedef声明一些数据类型,把它们单独放在一个文件中,然后需要用到它们的文件中用#include命令把它们包含进来。
-
有利于程序的通用和移植性。
-
typedef与define有相似之处,例如:typedef int count;#define Count int的作用都是Count代替int。但是,它们二者是不同的。
-
#define是在预编译时处理的,他只能看做是简单的字符串替换,而typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型,一个新的名称。
-
一个有分号(语句)一个没有分号(命令)
-