结构体与共用体
定义一个结构
- C语言给出了另一种构造数据类型-“结构(structure)”或叫“结构体”
- 相当于高级语言中的记录
- “结构”是一种构造类型,它是由若干“成员”组成的
- 每一个成员可以是一个基本数据类型或者又是一个构造类型
- “结构”既是一种构造而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它
struct 结构名{
// 成员表列
类型说明符 成员名1;
类型说明符 成员名2;
...
类型说明符 成员名n;
};
例:
struct stu{
int num;
char name[20];
char sex;
float score;
};
结构类型变量的说明
三种方法:
- 先定义结构,再说明结构变量
struct stu{
int num;
char name[20];
char sex;
float score;
}
struct stu boy1,boy2;
- 在定义结构类型的同时说明结构变量
struct stu{
int num;
char name[20];
char sex;
float score;
}boy1,boy2;
用宏定义使一个符号常量表示一个结构类型
#define STU struct stu
STU
{
int num;
char name[20];
char sex;
float score;
};
STU boy1,boy2;
- 直接说明结构变量(省去结构名)
struct
{
int num;
char name[20];
char sex;
float score;
}boy1,boy2;
结构嵌套结构
struct date{
int month;
int day;
int year;
};
struct{
int num;
char name[20];
char sex;
struct date birthday;
float score;
}boy1,boy2;
结构变量成员的表示方法
- 一般形式
结构变量名.成员名
- 成员变量本身又是一个结构,必须逐级找到最低的成员才能使用
boy1.birthday.moth
结构变量的赋值
#include<stdio.h>
void main(){
struct date{
int month;
int day;
int year;
};
struct{
int num;
char *name;
char sex;
struct date birthday;
float score;
}boy1,boy2;
boy1.num = 102;
boy1.name = "张 三";
printf("input sex and score:\n");
scanf("%c,%f",&boy1.sex, &boy1.score);
printf("input birthday:year-month-day\n");
scanf("%d-%d-%d",&boy1.birthday.year, &boy1.birthday.month, &boy1.birthday.day);
boy2=boy1;
printf("\nInformation:\n");
printf("Number=%d \nName=%s\n",boy2.num,boy2.name);
printf("Sex=%c \nScore=%f\n",boy2.sex,boy2.score);
printf("Birthday:%d-%d-%d\n",boy2.birthday.year,boy2.birthday.month,boy2.birthday.day);
}
结构变量的初始化
#include<stdio.h>
void main(){
struct stu{
int num;
char *name;
char sex;
float score;
}boy2,boy1={102,"Zhang San",'M',78.5};
boy2=boy1;
printf("Number=%d \nName=%s\n",boy2.num,boy2.name);
printf("Sex=%c \nScore=%f\n",boy2.sex,boy2.score);
}
结构数组的定义
数组元素也可以是结构类型
案例1
计算学生的平均成绩和不及格的人数
#include<stdio.h>
struct stu{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
void main(){
int i,c=0;
float ave,s=0;
for(i=0;i<5;i++){
s+=boy[i].score;
if(boy[i].score<60){
c+=1;
}
}
printf("s=%f\n",s);
ave=s/5;
printf("average=%f\ncount=%d\n",ave,c);
}
案例2
建立同学通讯录
#include<stdio.h>
#define NUM 3
struct mem{
char name[20];
char phone[11];
};
void main(){
struct mem man[NUM];
int i;
for(i=0;i<NUM;i++){
printf("N0.=%d\n",i+1);
printf("input name:\n");
gets(man[i].name);
printf("input phone:\n");
gets(man[i].phone);
}
printf("\nname\t\tphone\n\n");
for(i=0;i<NUM;i++){
printf("%s\t\t\t%s\n",man[i].name,man[i].phone);
}
}
结构指针变量的说明和使用
指向结构变量的指针
结构指针变量:一个指针变量当用来指向一个结构变量时,称为结构指针变量
结构指针变量中的值:所指向的结构变量的首地址
- 结构指针变量说明
struct 结构名 *结构指针变量名
- 赋值:把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量
pstu=&boy
- 访问
(*结构指针变量).成员名
结构指针变量->成员名
#include<stdio.h>
struct stu{
int num;
char *name;
char sex;
float score;
} boy1={102,"Zhang ping",'M',78.5}, *pstu;
void main(){
pstu=&boy1;
printf("Number=%d \nName=%s\n",boy1.num,boy1.name);
printf("Sex=%c \nScore=%f\n",boy1.sex,boy1.score);
printf("\n");
printf("Number=%d \nName=%s\n",(*pstu).num,(*pstu).name);
printf("Sex=%c \nScore=%f\n",(*pstu).sex,(*pstu).score);
printf("\n");
printf("Number=%d \nName=%s\n",pstu->num,pstu->name);
printf("Sex=%c \nScore=%f\n",pstu->sex,pstu->score);
}
指向结构数组的指针
#include<stdio.h>
struct stu{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
void main(){
struct stu *ps;
printf("No.\tName\t\t\tSex\tScore\t\n");
for(ps=boy;ps<boy+5;ps++){
printf("%d\t%s\t\t%c\t\t%f\t\n",ps->num,ps->name,ps->sex,ps->score);
}
}
结构指针变量作函数参数
#include<stdio.h>
struct stu{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
void ave(struct stu *ps){
int c=0,i;
float ave,s=0;
for(i=0;i<5;i++){
s+=ps->score;
if(ps->score<60){
c+=1;
}
}
printf("s=%f\n",s);
ave=s/5;
printf("average=%f\ncount=%d\n",ave,c);
}
void main(){
struct stu *ps;
void ave(struct stu *ps);
ps=boy; //定义说明结构指针变量ps,并把boy的首地址赋予它,使ps指向boy数组
ave(ps); // 以ps作为实参调用函数ave()
}
动态存储分配
- 在实际的编程中,往往会发生这种情况,即所需的内存空间取绝于实际输入的数据,而无法预先确定
- C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可以把不再使用的空间回收待用
分配内存空间函数malloc
- 在内存的动态存储区中分配一块长度为 size字节的连续区域
- 函数的返回值为该区域的首地址
(类型说明符 *)malloc(size)
类型说明符: 表示把该区域用于何种数据类型
(类型说明符*):表示把返回值强制转换为该类型指针
size:是一个无符号数
pc=(char *)malloc(100);
表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值指向该字符数组的指针,把该真值赋予指针变量pc.
分配内存空间函数 calloc
- 在内存动态存储区中分配 n 块长度为 size 字节的连续区域
- 函数的返回值为该区域的首地址
- calloc() 与 malloc() 函数的区别:仅在于一次可以分配n块区域.
(类型说明符 *)calloc(n,size)
ps=(struct stu*)calloc(2, sizeof(struct stu));
按 stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps
sizeof(struct stu)
是求 stu 的结构长度
释放内存空间函数 free
free(void *ptr)
- 释放ptr所指向的一块内存空间,ptr 是一个任意类型的指针变量,它指向被释放区域的首地址
- 被释放区应是 malloc 或 calloc 函数所分配的区域
案例
分配一块区域,输入一个学生数据
#include<stdio.h>
void main(){
struct stu{
int num;
char *name;
char sex;
float score;
}*ps;
ps=(struct stu*)malloc(sizeof(struct stu));
ps->num=102;
ps->name="Zhang ping";
ps->sex='M';
ps->score=62.5;
printf("Number=%d \nName=%s\n",ps->num,ps->name);
printf("Sex=%c \nScore=%f\n",ps->sex,ps->score);
free(ps);
}
链表
每一次分配一块空间可用来存放一个学生的数据,可称之为一个节点. 有多少个学生就应该申请分配多少块内存空间,也就是建立多少个节点
结构数组与链表的区别:
- 结构数组的弊端:
a. 预先不能准确把握学生人数,也就是无法确定数组大小
b. 而且当学生留级、退学也不能把该元素占用的空间从数组中释放出来 - 链表的优点(动态存储的方法):
a. 有一个学生就分配一个节点,无须预先确定学生的准确人数,某学生退学,可删去该节点,并释放节点占用的存储空间,从而节约了宝贵的内存资源
b. 用数组的方法必须占用一块连续的内存区域。而使用动态分配时,每个节点之间可以是不连续的(节点内是连续的).节点之间的联系可以用指针实现.
指针域:即在节点结构中定义一个成员项用来存放下一节点的首地址,这个用于存放地址的成员,常把它称为指针域
链表: 可在第一个节点的指针域内存入第二个节点的首地址,在第二个节点的指针域内又存放第三个节点的首地址,如此串连下去直到最后一个节点。最后一个节点因无后续节点连接,其指针域可赋值为0.这种连接方式,在数据结构中称为链表.
示例:
第0个节点称为 头节点
(head
),它存放第一个节点的首地址,它没有数据,只是一个指针变量. 以下每个节点都分为两个域,一个是数据域,存放各种实际数据,如学号、姓名、性别、成绩等;另一个域为指针域,存放下一节点的首地址.
struct stu{
int num;
int score;
struct stu *next;
};
枚举类型
- 在枚举类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围
- 枚举类型是一种基本数据类型,而不是一种构造类型
枚举类型的定义和枚举变量的说明
enum 枚举名{枚举值表}枚举变量1,枚举变量2
枚举类型变量的赋值和使用
- 枚举值是常量,不是变量。不能再程序中赋值语句再对它赋值
- 枚举元素本身由系统定义了一个表示序号的数据,从0开始顺序定义为0,1,2,… 如在weekday中,sun值为0,mon值为1, …, sat值为6
- 只能把枚举值赋予枚举变量,不能把元素的数据直接赋予枚举变量
案例1
#include<stdio.h>
void main(){
enum weekday {sun,mon,tue,wed,thu,fri,sat} a,b,c;
a=sun;
b=mon;
c=(enum weekday)2; // 一定要把数值赋予枚举变量,则必须用强制类型转换;
// 其意义是将顺序号为2的枚举元素赋予枚举变量c
printf("%d,%d,%d \n",a,b,c);
}
案例2
#include<stdio.h>
void main(){
enum body{a,b,c,d} month[31],j;
int i;
j=a;
for(i=1;i<=30;i++){
month[i]=j;
j++;
if(j>d) j=a;
}
for(i=1;i<=30;i++){
switch(month[i]){
case a:printf(" %2d %c\t\n",i,'a');break;
case b:printf(" %2d %c\t\n",i,'b');break;
case c:printf(" %2d %c\t\n",i,'c');break;
case d:printf(" %2d %c\t\n",i,'d');break;
default:break;
}
}
}
类型定义符 typedef
允许用户为数据类型取“别名”,使用 typedef
typedef 原类型名 新类型名
宏定义是在预处理完成的,而 typedef 则是在编译时完成的