H:用户建立自己的数据类型
(一) 结构体
定义结构体
Struct 结构体名
{
类型 数据1
类型 数据2
……
类型 数据N
}结构体变量名;
可以把结构体看做一个特殊的int类型的数据,只不过它里面又包含了多个类型的数据,结构体的初始化,引用如下所示。
方式一:#include<stdio.h>
struct student
{
int num;
char name[10];
double math;
};
int main()
{
struct student a;
char name[20]="qwert";//初始化姓名
a.num=1;
strcpy(a.name,name);/*用复制函数将姓名复制进入结构体,在初学时易a.name=”qwert”的错误*/
a.math=80.5;
printf("num=%d name=%s math=%lf",a.num,a.name,a.math);
getch();
return 0;
}
方式二:
#include<stdio.h>
struct student
{
int num;
char name[10];
double math;
}a={1,"qwert",80.5};
int main()
{
printf("num=%d name=%s math=%lf",a.num,a.name,a.math);
getch();
return 0;
}
方式三:
#include<stdio.h>
struct student
{
int num;
char name[10];
double math;
};
int main()
{
struct student a,*b;
char name[20]="qwert";
a.num=1;
strcpy(a.name,name);
a.math=80.5;
b=&a;
printf("num=%d name=%s math=%lf",b->num,b->name,b->math);
getch();
return 0;
}
解析:1):方式一和方式二差别不是太大,一看就懂了,方式三是结构体和指针的结合使用。
2):要学会”.”运算符和在引用含有指针变量的结构体时用“->”运算符。
3):此外还要知道结构体所占字节数;上面例子中的结构体所占字节是24,它是基本数据类型的整数倍。在此例子中double类型数据为基本类型,4+10+8=22,它是基本类型的整数倍,所以结果是24。
4):知道了结构体如何定义、引用就会知道公用体和枚举的定义引用方法,因为它们是相同的。
(二) 共用体(联合体)
共用体的定义,引用方式和结构体相同。
共用体的特殊之处
1):所谓共用体,就是申请一块内存,一起共享这块内存。
2):这块内存的大小是共用体中最大类型的数据所占的内存。
#include<stdio.h>
union student
{
int num;
char name[10];
double math;
};
int main()
{
union student a,*b;
char name[20]="qwert";
a.num=1;
strcpy(a.name,name);
a.math=80.5;
b=&a;
printf("num=%d name=%s math=%lf\n",b->num,b->name,b->math);
printf("%d ",sizeof(union student));
getch();
return 0;
}
在该例子中试图给该公用体所有的变量赋值,但结果以最后一次赋值的结果为最终结果,这就是共用体的妙处。它是以最后一次赋值的结果为最终结果,因为它们公用一块内存,当该内存再次写入以前写入的就被覆盖了。
它所占内存的大小是16字节,以为char name[10]为10个字节,但它是基本数据类型double的整数倍,所以是16字节
(三) 枚举
枚举的定义,引用方式和结构体相同。
枚举的特殊之处
1):编译系统把枚举元素当做常量处理,从零开始,如果对某个枚举元素赋值,该枚举元素就为所赋的值,它以后的枚举元素就在它的基础上依次增加。
2)它可以用做判断比较。
(四) 链表
所谓链表,就是一个数据链,它能把数据穿在一起,可以做到抓住头,找到尾,能把所有数据过滤一遍,它非常重要。
在学习链表的过程中,要结合例子,理解链表。理解某些主要程序语句的意思,这些主要程序语句是把链表穿起来的关键,其他的能看懂就可以,因为这些主要程序语句的逻辑新很强,理解起来很有难度,我在学习链表的过程中,曾把例子抄写下来,一遍又一遍,每一遍的抄写都是我对链表的理解更深一层。把这个链表模板给出以便学习思考。
蓝色部分为主要程序语句,它是和动态内存分配结合在一起的,用到结构体,指针,循环,等的知识。
#include <stdio.h>
#include <malloc.h>
//保留数据的节点
typedef struct node
{
int data;
struct node *next;
}Node;
//链表的管理节点,包括表头和链表节点的数量
typedef struct nodectrl
{
Node *head;
int num;
}NodeCtrl;
//申请管理节点的空间,并初始化表头和节点数量值
NodeCtrl *CreateCtrl()
{
NodeCtrl *nc = (NodeCtrl*)malloc(sizeof(NodeCtrl));
if(nc == NULL)
{
puts("Create NodeCtrl failed!");
return NULL;
}
//由于刚建立管理节点,链表中没有任何节点
nc->head = NULL;
nc->num = 0;
return nc;
}
//根据管理节点内存位置,加入一个新的数据(数据节点)
int AddData(NodeCtrl *nc,int data)
{
Node *p,*q;
if(nc == NULL)
return -1;
//新申请一个Node节点,用来保留data数据
q = (Node*)malloc(sizeof(Node));
if(q == NULL)
return -1;
//保留数据
q->data = data;
q->next = NULL;
//找到链表的头节点,准备插入数据
p = nc->head;
//插入数据(头插法)
if(p != NULL)
q->next = nc->head;
nc->head = q;
//由于增加了一个节点,所以节点数量需要加1
nc->num++;
return 0;
}
//根据管理节点,显示表头所在链表中的所有存储值
void Display(NodeCtrl *nc)
{
Node *p = NULL;
if(nc == NULL)
return;
printf("Current list has %d data!\n",nc->num);
//num记录的是节点数量,如果为0,表示是空表,否则有数据
if(nc->num > 0)
{
//找到表头
p = nc->head;
//如果节点不空
while(p != NULL)
{
printf("%d\n",p->data);
p = p->next;
}
}
}
//释放管理节点保留的链表,先释放链表,在释放管理节点
void FreeNodeCtrl(NodeCtrl *nc)
{
//定义两个临时指针,q负责保留上次的节点,p负责向下寻找下一个节点
Node *p,*q;
if(nc == NULL)
return;
//如果num大于0,表示链表中有大于等于1个节点,所以必须释放
if(nc->num > 0)
{
//找到管理节点保存的链表表头
p = nc->head;
//如果节点不空
while(p != NULL)
{
//先保留p节点的地址
q = p;
//p节点向下移动,此时q在p上一个节点位置
p = p->next;
//释放p节点的上一个节点
free(q);
}
}
//链表节点释放完毕,将管理节点的数据清0
nc->head = NULL;
nc->num = 0;
//释放管理节点
free(nc);
}
int main()
{
int i,data;
NodeCtrl *nc1 = CreateCtrl();
NodeCtrl *nc2 = CreateCtrl();
for(i=0;i<3;i++)
{
scanf("%d",&data);
AddData(nc1,data);
}
for(i=0;i<20;i++)
AddData(nc2,i);
Display(nc1);
FreeNodeCtrl(nc1);
Display(nc2);
FreeNodeCtrl(nc2);
return 0;
}
(五) 宏定义和typedef类型
typedef可以创造新的数据类型,用以避免数据类型不足时产生的尴尬。就我现在所接触到的最多的就是这样的类型:
1):Typedef struct student
{
Int num;
Double score;
Char name;
}stu;
这定义了一个结构体类型,stu内包含了多个数据类型,在程序中若出现stu,就表示这样的一个数据类型;
#include<stdio.h>
typedef int (*fp) (int x,int y);
int call(fp p,int x,int y)
{
return p(x,y);
}
int add(int x,int y)
{
return x+y;
}
int main()
{
fp p;
printf("%d",call(add,2,3));
getch();
return 0;
}
此例子用到typedef创建新类型,和函数回调以及指针的知识
2):#define n 100;
宏定义的define是典型的“后替前”,即当程序中出现n时,就会用100代替,用在程序中可以做到“一改全改”的效果,避免了程序在某些情况下繁琐的修改过程,节省了时间,提高了效率。
简单的代换
#include<stdio.h>
#define MYINT int x,
int main()
{//当遇到MYINT时,int x,就代替了MYINT
MYINT a,b;//后代替前
x=2;
printf("x=%d\n",++x);
getch();
return 0;
}
通过下面这个例子大家可以理解代替的深层含义
#include<stdio.h>
#define swap(x,y) x*y
int main()
{
int a=3,b=7;
printf("a=%d\na=%d",swap(a,b),swap(a+1,b));
getch();
return 0;
}