目录
一、定义和使用结构变量
1、建立结构体类型
用户建立不同类型数据组成的组合数据结构,称为结构体,用于反应不同变量间的联系。
定义结构体的一般形式为:
struct 结构体名
{
类型名 成员名;
};
struct Student
{
int num;
char name[20];
char sex;
int age;
float score;
char adder[30];
};
花括号内是该结构体所包含你的子项,称为结构体的成员,对各成员都应进行类型的声明。
此外,成员还可以属于另一个结构体类型
struct Date
{
int month;
int day;
int year;
};
struct Student
{
int num;
char name[20];
char sex;
int age;
struct Date birthday;//属于上一个结构体类型
char addr[30];
};
2、定义结构体类型变量
前面建立了一个结构体类型,其相当于一个模型,没有变量,等价于int,float,char这一类,因而应当定义结构体类型的变量,并在其中存放具体的数据。定义结构体类型变量有三种方法:
(1)先声明结构体类型,再定义该类型变量
struct Date
{
int month;
int day;
int year;
};//先声明
struct Date birthday;//再定义
(2)在声明类型的同时定义变量
struct Date
{
int month;
int day;
int year;
}date1,date2;//date1,date2即为定义的结构体类型变量
(3)不指定类型名而直接定义结构体类型变量
struct
{
int month;
int day;
int year;
}date1,date2;//date1,date2即为定义的结构体类型变量,但没有类型名
3、结构体变量的初始化和引用
(1)定义结构体变量时,可以对它初始化,赋予初始值再引用这个变量。引用的格式为:
结构体变量名.成员名
例:将学生信息放在一个结构体内,输出该学生的信息
#include <stdio.h>
#include <stdlib.h>
int main()
{
struct Student //声明结构体类型
{
long int num;
char name[20];
char sex;
char addr[20];
}a={10101,"LiLin",'M',"123Beijing Road"};//初始化结构体变量
printf("NO.%ld\nname:%s\nsex:%c\naddress:%s\n",a.num,a.name,a.sex,a.addr);
}
(2)若成员本身又属于另外一个结构体类型,则要用若干成员运算符,一级一级地找到最低一级的成员。只能对最低级地成员进行赋值。
student1.num //结构体student1中的成员num
student1.birthday.month//结构体变量student1中成员birthday中的成员month
(3)对结构体变量的成员可以像普通变量一样进行各种运算
student1.score=student2.score//赋值
sum=student1.score+student2.score//加法
student1.age++ //自加
(4)同类的结构体变量可以相互赋值
student1=student2
二、使用结构体数组
定义结构体数组一般形式为:
(1)struct 结构体名
{成员表列} 数组名[数组长度];
(2)先声明一个结构体类型(如struct Person),再用此类型定义结构体数组:
struct Person leader[3];
struct Date
{
int month;
int day;
int year;
}birthday[3]={2001,1,1,2022,1,2,2020,1,3};//定义结构体数组并初始化
或声明后再定义结构体数组
struct Date birthday[3]={2001,1,1,2022,1,2,2020,1,3};
例:有三个候选人,10个选民,每个选民只能投一票,编写一个投票程序,先后输入候选人名字,最后输出各人得票结果
#include <stdio.h>
#include <stdlib.h>
struct Person //声明结构体类型 struct Person
{
char name[20];
int count;
}leader[3]={"Li",0,"Zhang",0,"Sun",0}; //定义结构体数组并初始化
int main()
{
int i,j;
char leader_name[20];
for(i=1;i<=10;i++)
{
scanf("%s",leader_name); //候选人名字
for(j=0;j<3;j++)
{
if(strcmp(leader_name,leader[j])==0) leader[j].count++;//与结构体数组进行匹配
}
}
printf("\nresult:\n");
for(i=0;i<3;i++)
{
printf("%5s:%d\n",leader[i].name,leader[i].count);
}
return 0;
}
三、结构体指针
1、指向结构体变量的指针
指向结构体对象的指针变量既可以指向结构体变量,也可以指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。
struct Student *pt;
例:使用结构体指针输出学生信息
#include <stdio.h>
#include <stdlib.h>
int main()
{
struct Student
{
long num;
char name[20];
char sex;
float score;
} ;
struct Student stu_1;
struct Student *p;
p=&stu_1;
stu_1.num=10101;
strcpy(stu_1.name,"fangfang");//使用字符串复制函数给stu_1.name赋值
stu_1.sex='M';
stu_1.score=100;
printf("No:%ld\nname:%s\nsex;%c\nscore:%5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
printf("No:%ld\nname:%s\nsex;%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score); //使用两种方式输出学生信息
return 0;
}
如果p指向一个结构体变量stu,以下三种用法等价
(1)stu.成员名 如 stu.num
(2)(*p).成员名 如 (*p).num
(3)p->成员名 如 p->num
2、指向结构体数组的指针
例:有三个学生信息,存放在结构体数组中,使用指向结构体数组的指针输出学生信息
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int num;
char name[20];
char sex;
int age;
} ;
struct Student stu[3]={{10101,"Li",'M',18},{10102,"Fang",'M',21},{10103,"Wang",'F',20}}; //定义结构体数组并初始化
int main()
{
struct Student *p;
printf("NO. Name sex age\n");
for(p=stu;p<stu+3;p++)
{
printf("%-6d%-20s%2c%4d\n",p->num,p->name,p->sex,p->age);//输出结果
}
return 0;
}
3、用结构体变量和结构体变量的指针作函数参数
将一个结构体变量的值传递给另一个函数,有3种方法:
(1)用结构体变量成员作参数
(2)用结构体变量作实参
(3)用指向结构变量(或数组元素)的指针作实参,将结构体变量(或数组元素)的地址传给形参
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int num;
...
}
int main()
{
struct Student max(struct Student stu[]);
struct Student stu[N],*p=stu;
max(p);
}
struct Student max(struct Student stu[])
{
......
return stu[m];
}
四、用指针处理链表
1、什么是链表
链表是一种重要的数据结构,能够进行动态的存储分配,根据需要开辟内存空间,相比于事先定义长度的数组,应用更为灵活。
链表有一个“头指针”变量,如上图,以head表示,其存放一个地址,指向第一个元素,链表中,每一个元素称为一个“结点”,每个节点包括两个部分:(1)用户需要用到的实际数据;
(2)下一个结点的地址。当指向最后一个元素后,该元素不在指向其他元素,称之为“表尾”,它的地址部分存放一个“NULL”(空地址),链表到此结束。由上图知,链表中各个元素是不连续的,因而若无“头指针”,整个链表无法访问。创建链表使用结构体变量最合适。设计的结构体类型如下:
struct Student
{
int num;
float score;
struct Student *next;//next 是指针变量,指向结构体变量
};
2、建立简单的静态链表
例:创建一个由3个学生数据的结点组成的链表,并输出各结点中的数据
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int num;
float score;
struct Student *next;//next 是指针变量,指向结构体变量
};
int main()
{
struct Student a,b,c,*head,*p;//定义3个结构体变量作为链表结点
a.num=10101;a.score=89.5; //初始化结点a,b,c
b.num=10102;b.score=90;
c.num=10103;c.score=91;
head=&a;//将a地址赋给头指针
a.next=&b;//将b的起始地址赋给a结点的next成员
b.next=&c;//将c的起始地址赋给b结点的next成员
c.next=NULL;//c结点的next成员不存放其他结点地址
p=head;//使得p指向a结点
do
{
printf("%ld%5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);//输出链表
return 0;
}
3、建立动态链表
前面静态链表是在已经创建好结点后进行链表输出,而动态链表是指程序从无到有建立一个链表,即一个一个的开辟结点和输入结点数据,并建立起前后相链的关系。下面通过一个例子来展示。
例:写一个函数建立有3名学生数据的单向动态链表
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct Student)
struct Student
{
long num;
float score;
struct Student *next;//next 是指针变量,指向结构体变量
};
int n;
struct Student *creat(void) //定义返回一个指向链表表头的指针
{
struct Student *head;
struct Student *p1,*p2;
n=0;
p1=p2=(struct Student *)malloc(LEN);//先开辟一个新结点,p1,p2指向该结点
scanf("%ld,%f",&p1->num,&p1->score);//输入结点数据
head=NULL;//初始化表头
while(p1->num!=0)//当第一个结点输入数据不为零时进入循环
{
n=n+1;
if(n==1)head=p1; //若n等于1,则为第一次进入,将第一个结点的地址赋给头指针
else p2->next=p1;//若n不为1,则 将p2的next成员指向p1所指向的结点
p2=p1;
p1=(struct Student *)malloc(LEN);//开辟新结点后,将p1指向新结点
scanf("%ld,%f",&p1->num,&p1->score);//输入新结点的学生数据
}
p2->next=NULL//当p1->num等于0时,代表输入结束,则将p2指向空地址
return(head);//返回链表表头
}
int main()
{
struct Student *pt;
pt=creat();//调用函数
printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);//输出第一个结点学生数据
return 0;
}
4、输出链表
写一个函数用于输出整个链表
struct Student
{
long num;
float score;
struct Student *next;//next 是指针变量,指向结构体变量
};
void print(struct Student *head)//定义print函数
{
struct Student *p;
printf("\nNow,These %d records are:\n",n);
p=head;使p指向第一个结点
if(head!=NULL)//若不是空表
{
do
{
printf("%ld%5.1f\n",p->num,p->score);//输出结点存储的数据
p=p->next;//指向下一个结点
}while(p!=NULL);
}
}
五、共用体类型
1、什么是共用体类型
共用体类型指的是同一段内存单元可以存放不同类型的变量。存储的变量虽然字节可能不同,但起始地址均相同,因而后一个数据会覆盖前面数据。
定义共用体类型变量的一般形式为:
union 共用体名
{
成员表列
}变量表列;
当然,和结构体类似,共用体变量还有其他定义形式是
union Date
{
int i;
char ch;
float f;
}a,b,c;
或
union Date
{
int i;
char ch;
float f;
};
union Date a,b,c;
或
union
{
int i;
char ch;
float f;
}a,b,c;
共用体变量和结构体变量的不同:
结构体变量所占用的内存长度是各成员占的内存长度之和,每个成员均有自己的内存空间;而共用体变量所占的内存长度等于最长的成员的长度
2、引用共用体变量的方式
共用体变量的引用方式和结构体的相同:
a.i;
a.ch;
a.f;
3、共用体类型数据的特点
(1)同一个内存段可以用来存放几种不同类型的成员,但是在每一个瞬间只能存放其中一个成员。
#include <stdio.h>
#include <stdlib.h>
union Date
{
int i;
char ch;
float f;
}a;
int main()
{
a.i=97;
printf("%d\n",a.i); // 输出a.i,为97
printf("%c\n",a.ch);//97对应字符a,则输出a.ch时,输出字符'a'
printf("%f\n",a.f);//数值为0,则输出0.000000
return 0;
}
(2)可以对共用体进行初始化,但初始化表中只能有一个变量
union Data
{
int i;
char ch;
float f;
}a={1,'a',1.5};//不能初始化三个成员
union Data a={16};//对第一个成员初始化
union Data a={.ch='j'};//允许对指定成员进行初始化
(3)给共用体一个成员赋值后,该数据会覆盖上一个数据
六、使用枚举类型
若一个变量只有几种可能存在的值,则可定义为枚举类型,所谓枚举,就是指把所有可能存在的值均列出来,变量的值只能在这范围中。
声明枚举类型的一般形式为:
enum [枚举名]{枚举元素列表}
enum Weekday {sun,mon,tue,wed,thu,fri,sat};
enum Weekday workday,weekend;
每一个枚举元素均代表一个整数,C语言编译时按定义时的顺序默认它们的值为0,1,2,3,,,当然,也可以在定义枚举类型时显示地指定数值,如enum Weekday {sun=0,mon=7,tue,wed,thu,fri,sat};之后顺序加1,sat为12。
七、用typedef声明新类型名
除了可以直接使用C提供的标准类型名(如int,char,float,double和long等)和程序编写者自己声明地结构体,共用体,枚举类型外,还可以使用typedef指定新的类型名来代替已有的类型名。有以下两种情况:
1、简单地用一个新的类型名代替原有的类型名
typedef int Integer;
typedef float Real;
......
2、命名一个简单的类型名代替复杂的类型表示方法
(1)命名一个新的类型名代表结构体类型
typedef struct
{
int month;
int day;
int year;
}Date;
Date birthday;
Date *p;
(2)命名一个新的类型名代表数组类型
typedef int Num[100];//声明Num为整形数组类型名
Num a;//定义a为整型数组名,有100个元素
(3)命名一个新的类型名代表指针类型
typedef char * String;//声明String为字符指针类型
String p,s[10];//定义p为字符指针变量,s为字符指针数组
(4)命名一个新的类型名代表指向函数的指针类型
typedef int(*pointer)()//声明pointer为指向函数的指针类型,该函数返回整型值
pointer p1,p2;//p1,p2为pointer类型指针变量
归纳:
(1)先按定义变量方法写出定义体(如:int i;)
(2)将变量名换成新类型名(如:将i换成Count)
(3)在最前面加typedef(如:typedef int Count)
(4)然后用新类型名去定义变量。
本文章总结于谭浩强的C语言程序设计(第五版),并加上了自己的一些理解。
欢迎大家提出问题并指正~