c语言解析系列(6)用户建立自己的数据类型

来源:互联网 发布:网络名称侵权司法解释 编辑:程序博客网 时间:2024/06/05 02:14

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节点向下移动,此时qp上一个节点位置

           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;   

}

 

原创粉丝点击