C语言的结构体与共同体

10.1 结构体概述
将多个不同数据类型的数据组织成一个组合项的数据结构称为结构体。如
struct student{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
10.2 定义结构体类型变量的方法
一 先定义结构体类型再定义变量名
1 如struct student student1, student2;
则student1和student2被定义成结构体student的数据结构。
2 通常用一个符号常量来替代结构体类型。如#define STUDENT struct student
3 如果程序规模比较大,通常将结构体类型定义集中在一个头文件。
二 在定义类型的同时定义变量,如
struct student{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1, student2;
三 直接定义结构类型变量,如
struct{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1, student2;
10.3 结构体类型变量的引用
1 不能将结构体变量作为一个整体进行输入输出。只能以结构体变量名.成员名的方式分别输出结构体中的各个成员,如student1.num。
2 如果结构体成员也是一个结构体,那么需要若干成员运算符一级一级找到最低级的成员进行赋值、存取或运算,如student1.birthday.year。
3 成员变量可以象普通变量一样进行运算。
4 可以引用成员的地址,也可以引用结构体的地址。
10.4 结构体变量的初始化
1 对外部存储类型的结构体变量进行初始化
struct student
{
long int num;
char name[20];
char sex;
char addr[20];
} student1 = {89031, “Li Lin”, ‘M’, “123 Beijing Road”};
main()
{}
2 对静态存储类型的结构体变量进行初始化,上例中的初始化部分可以放到main函数中。
10.5 结构体数组
结构体数组其每个数组元素都是一个结构体类型数据,都分别包含各个成员项。各个数组元素在内存中连续存放。
一 结构体数组的定义
struct student
{
int num;
……
} stu[3];
二 结构体数组的初始化
struct student stu[] = {{…}, {…}, …, {…}};
10.6 指向结构体类型数据的指针
结构体变量的指针指向该结构体变量占据的内存段的起始地址。指针变量也可以用来指向结构体数组中的数组元素。
10.6.1 指向结构体变量的指针
struct student stu_1;
struct student *p;
p = &stu_1;
printf(“\nNo.:%ld\nname:%s\nsex:%c\nscore:%f\n”, (*p).num, (*p).name, (*p).sex, (*p).score);
(*p).num代表指针变量p指向的结构体的成员项num。
C语言中规定可以将(*p).num改用p->num表示,->为指向运算符。
10.6.2 指向结构体数组的指针
1 struct student stu[] = {{…}, {…}, …, {…}};
struct student *p;
p = stu;
定义stu为结构体数组,将指针变量p指向结构体数组stu的首地址(也是结构体stu[0]的首地址)。
p++意味增加结构体数组stu一个数组元素(即一个结构体student)所占的字节数,使指针变量p指向stu[1]的首地址。
2 (++p)->num,意思是先使p加一然后指向结构体成员num,即指向stu[1].num。
(p++)->num,意思是先得到p->num的值,即stu[0].num。然后使p加一,指向stu[1]的首地址。
3 p已经定义为指向结构体student类型,则不能用来指向其他类型数据,除非使用强制类型转换。
p = (struct student *)&stu.name
10.6.3 用指向结构体的指针作函数参数
10.7 用指针处理链表
10.7.1 链表概述
链表有一个头指针变量head,该指针变量指向链表的第一个元素。链表的每个有效元素都包含至少两个部分,一个是用户需要的实际数据,另一个是一个指向链表下一个元素的指针变量。链表表尾的地址部分存放空地址NULL表示链表的结束。
链表中的各个元素在内存中不是连续存放的,而是通过指针变量一一串连的。
可以通过结构体变量的方式来建立链表。
Struct student
{
int num;
float score;
struct student *next;
};
链表结构通过以下三个系统函数动态分配和释放存储空间。
1 malloc(size),在内存动态存储区分配一个长度为size的连续空间。函数返回一个指针变量,指向被分配的连续空间的起始地址。如果分配失败,返回0。
2 calloc(n, size),在内存动态存储区分配n个长度为size的连续空间。返回分配域的起始地址;分配失败的话返回0。
3 free(ptr),释放指针变量ptr指向的内存区。Calloc/malloc分配内存空间之后必须用free进行释放,否则会造成野指针,引发不能预期的错误结果。
10.7.2 建立链表
// 定义NULL=0,代表空地址
#define NULL 0
// 定义LEN就是结构体student占用内存空间的长度
#define LEN sizeof(struct student)
struct student
{
int num;
float score;
struct student *next;
};
int n;
// 定义函数creat,返回一个指向结构体student类型的指针变量,实际就是链表起始地址
struct student *creat()
{
struct student *head;
struct student *p1, *p2;
n = 0;
// 为指针变量p1、p2分配内存空间,p1是新引入的结点,p2是链表的最后一个结点
p1 = p2 = (struct student*)malloc(LEN);
scanf("%ld, %f", &p1->num, &p1->score);
head = NULL;
// 学号为0代表链表建立结束
while(p1->num != 0)
{
n = n+1;
if (n == 1)
// 如果链表中只有一个结点,那么将链表起始地址head直接指向新引入的结点p1
head = p1;
else
// 否则使链表最后一个结点中的指针变量指向新引入的p1,意思就是将p1接入链表
p2->next = p1;
// 将p2指向新引入的结点p1,意思就是使p2继续指向链表最后一个结点
p2 = p1;
// 为p1分配新的内存空间并引入下一个结点的内容
p1 = (struct student*)malloc(LEN);
scanf("%ld, %f", &p1->num, &p1->score);
}
// 由于链表建立结束,p2是链表最后一个结点,所以使p2中的指针变量指向NULL,代表表尾
p2->next = NULL;
// 返回链表的首地址
return(head);
}
10.7.3 输出链表
void print(struct student *head)
{
struct student *p;
printf("\nNow, These %d records are:\n", n);
// 是指针变量p指向链表的首地址
p = head;
if (head != NULL)
{
do{
// 打印链表当前结点的内容,然后通过当前结点中的指针变量指向下一个结点
printf("%ld %5.1f\n", p->num, p->score);
p = p->next;
}
// 链表表尾结点中的指针变量指向空地址NULL,所以当p指向NULL的时候说明到了表尾
while(p != NULL);
}
}
10.7.4 对链表的删除操作
struct student *del(struct student *head, int num)
{
struct student *p1, *p2;
if (head == NULL)
{
// 如果首地址head是空地址,说明链表是空的,直接跳转到end
printf("\nlist null!\n");
goto end;
}
p1 = head;
while (num != p1->num && p1->next != NULL)
{
// 当前指向的结点的学号并非需要的学号并且链表还有下一个结点
// while循环结束时,p1指向的结点就是需要删除的结点,p2是前一个结点
// 或者没有找到需要删除的结点
p2 = p1;
p1 = p1->next;
}
if (num == p1->num)
{
// p1指向的结点就是需要删除的结点
if (p1 == head)
// 需要删除的结点是第一个结点,那么让首地址head指向第二个结点即可
head = p1->next;
else
// 使前一个结点p2中的指针变量指向p1的下一个结点,意思就是使链表跳过了p1结点
p2->next = p1->next;
printf("delete:%ld\n", num);
n = n-1;
}
else
printf("%ld not been found!\n", num);
end:
return(head);
}
10.7.5 对链表的插入操作
struct student *insert(struct student *head, struct student *stud)
{
struct student *p0, *p1, *p2;
p1 = head;
p0 = stud;
if (head == NULL)
{
// 如果目标链表是空链表,那么将首地址head直接指向需要插入的结点p0并使p0中的指针变量指向空地址即可
head = p0;
p0->next = NULL;
}
else
{
while ((p0->num > p1->num) && (p1->next != NULL))
{
// 因为需要按学号从小到大排序,所以当需要插入的结点p0的学号大于当前结点p1的学号并且链表还有下一个结点的时候,执行while循环
// 循环结束后,p1是学号大于p0的第一个结点,p2是p1的前一个结点。也就是说,p0需要插入到p2和p1之间
// 或者是链表中的所有结点的学号都小于等于p0的学号,那么p0应该插入到链表的表尾
p2 = p1;
p1 = p1->next;
}
if (p0->num <= p1->num)
{
// p0应该插入到p2和p1之间
if (head == p1)
{
// 如果p1是第一个结点,使首地址指向p0,p0中的指针变量指向p1
head = p0;
p0->next = p1;
}
else
{
// 使p2中的指针变量指向p0,p0中的指针变量指向p1,意思就是使p0插入到p2和p1之间
p2->next = p0;
p0->next = p1;
}
}
else
{
// 将p0插入到链表的表尾
p1->next = p0;
p0->next = NULL;
}
}
n = n+1;
return(head);
}
10.8 共用体
10.8.1 共用体的概念
共用体就是将几种不同类型的变量存放到同一段内存单元中,不同的变量类型占据的内存空间不同,但都是从同一个地址开始存放。
union 共用体名
{
成员表列
}变量表列;
结构体变量占据的内存长度是各个成员项占据的内存长度之和。共用体变量占据的内存长度是最长的成员项所占据的内存长度。
10.8.2 共用体变量的引用方式
不能引用共用体变量自身,只能引用共用体变量中的成员项。
10.8.3 共用体类型数据的特点
1 同一内存段可以用来存放几种不同类型的成员,但是每一瞬间只能存放其中一种。
2 共用体变量中起作用的是最后一次存放的成员。
3 共用体变量的地址和其各个成员的地址是同一地址。
4 不能对共用体变量名赋值,不能通过引用变量名来得到成员项的值。不能在定义共用体变量时进行初始化。
5 不能把共用体变量作为函数参数或返回值,但可以使用指向共用体变量的指针。
6 共用体类型可以出现在结构体中,也可以定义共用体数组。
10.9 枚举类型
定义为枚举类型的变量只能有几种可能的值。例如enum weekday{sun, mon, tue, wed, thu, fri, sat}workday, week_end;
{}中的sun、mon…称为枚举元素或枚举常量,不能对其进行赋值操作。
编译时对枚举常量按定义顺序依次初始化为0,1,2…
可以在定义的时候人为指定枚举常量的值,如enum weekday{sun=7, mon=1, tue, wed, thu, fri, sat};
定义为枚举类型的变量不能直接赋值,而只能通过强制类型转换才能赋值,如workday = (enum weekday)2;
10.10 用typedef定义类型
可以用typedef定义的新的类型名来代替已有的类型名,如typedef float REAL;

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zhuhe19750218/archive/2008/10/12/3065775.aspx

上一篇: C++运算符重载   下一篇: 在线教程 Effective C++(第三版 中文翻译)

提交疑问

回顶部