C语言——用户自己建立数据类型

1 定义和使用结构体变量

C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体。在其他一些高级语言中称为"记录"

可以在程序中自己建立一个结构体类型。例如:
struct Student
{
int num; //学号为整型
char name[20]; //姓名为字符串
char sex; //性别为字符型
int age //年龄为整型
float score; //成绩为实型
char addr[30]; //地址为字符串
}; //注意最后有一个分号
上面由程序设计者指定了一个结构体类型struct Student(struct是声明结构体类型时必须使用的关键字,不能省略)

声明一个结构体类型的一般形式为:
struct 结构体名
{成员列表};
“成员列表"也称为"域表”,每一个成员是结构体中的一个域,成员名命名规则与变量名相同
注:
1 结构体类型并非只有一种,而是可以设计出许多种结构体类型,各自包含不同的成员

2 成员可以属于另一个结构体类型。例如:
struct Date
{
int month;
int day;
int year;
};
struct Student
{
int num;
char name[20];
char sex;
int age;
struct Date birthday; //成员birthday属于struct Date类型
char addr[30];
};

2 定义结构体类型变量

2.1 先声明结构体类型,再定义该类型的变量
如: struct Student student1,student2;
struct Student //声明结构体类型名
student1,student2; //定义该类型的变量

2.2 在声明类型的同时定义变量
这种定义方法的一般形式为:
struct 结构体名
{
成员列表

}变量名列表;

例如:
struct Student
{
int num;
char name[20];
char sex;
int age;
char addr[30];
}student1,student2;

2.3 不指定类型名而直接定义结构体类型变量
其一般形式为;
struct
{
成员列表

}变量名表列;

指定了一个无名的结构体类型,它没有名字(不出现结构体名)。显然不能再以此结构体类型去定义其他变量。这种方式用的不多

注:
1 结构体类型与结构体变量是不同的的概念,只能对变量进行赋值、存取或运算,不能对一个类型赋值、存取或运算。在编译的时候,对类型是不分配存储空间的,只对变量分配空间;
2 结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象;
3 对结构体变量中的成员 (即“域”),可以单独使用,它的作用与地位相当于普通变量。

3 结构体变量的初始化和引用

在定义结构体变量的时候,可以对它进行初始化,即赋初值,然后可以引用这个变量
如:
#include<stdio.h>
int main()
{
struct Student
{
long int numm;
char name[20];
char sex;
char addr[20];
}a={10101,“Li Lin”,‘M’,“123 Beijing Road”}; //定义结构体变量a并初始化
printf(“NO.:%ld\nname:%s\nsex:%c\naddress:%s\n”,a.name,a.name,a.sex,a.addr);
return 0;
}
注:
1 在定义结构体便来哪个是可以对它的成员进行初始化。C99标准允许对某一成员初始化,如: struct Student b={.name=“Zhang Fang”}; //".name"隐含代表结构体变量b中的成员b.name.其他未被指定初始化的数值型成员被系统初始化为0,字符型成员被系统初始化为’\0’,指针型成员被系统初始化为NULL。

2 可以引用结构体变量中成员的值,引用方式为:结构体变量名.成员名。“.”是成员运算符,其优先级最高
不能通过输出结构体变量名来来达到结构体变量所有成员的值
下面用法不正确:
printf(“%s\n”,student1);
只能对结构体变量的各个成员分别进行输入和输出

3 如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员,且只能对最低一级的成员进行赋值或存取以及运算;

4 对结构体变量的成员可以像普通变量一样进行各种运算

5 同类的结构体变量可以互相赋值

6 可以引用结构体变量成员的地址,也可以引用结构体变量的地址

4 使用结构体数组

所谓结构体数组,是指数组中的每个元素都是一个结构体

定义结构体数组的一般形式是:
struct 结构体名
{成员列表} 数组名[数组长度];
如:先声明一个结构体类型,然后再用此类型定义结构体数组
struct Person leader[3]; //leader是结构体数组名,对结构体初始化的形式是在定义数组的后面加上:={初值表列};如:
struct Person leader[3]={“Li”,0,“Zhang”,0,“Sun”,0};

5 结构体指针

5.1 指向结构体变量的指针
所谓结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针(如果将一个结构体变量的起始地址存放在一个指针变量中,那么这个指针变量就指向该结构体变量)

指向结构体对象的指针变量即可指向结构体变量,也可指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。如;
struct Student * pt; //pt可以指向struct Student类型的变或数组元素

如果p指向一个结构体变量stu,以下3种用法等价:
1 stu.成员名(如stu.num);
2 (*p).成员名(如(*p).num);
3 p->成员名(如p->num)

5.2 指向结构体数组的指针
可以用指针变量指向结构体数组的元素。例:

include <stdio. h>

struct Student //声明结构体类型 struct Student
{int num;
char name[20];
char sex;
int age;
};
struct Student stu[3]= {{10101,“Li Lin’,‘M’,18},(10102,“Zhang Fang”,‘M’, 19},
{10104,“Wang Min’,‘F’,20}};//定义结构体数组并初始化
int main()
{ struct Student * p; //定义指向 struct Student 结构体变量的指针
printf(” No. Name sex age)n”);
for (p=stu;p<stu+3;p十十)
printf(”%5d %-20s %2¢ %4d\n”,p->num, p->nare, D->sex, p->age);
//输出结果
return 0;
}

5.3 用结构体变量和结构体变量的指针作函数参数
将一个结构体变量的值传递给另一个函数,有3个方法:
<1> 用结构体变量的成员作参数。例如,用stu[1].num作函数参数,将实参值传给形参,属于"值传递"方式,应注意实参与形参的类型保持一致

<2>用结构体变量作实参。采用的也是"值传递"的方式,将结构体变量所占的内存单元的内容全部按顺序传递给形参,形参必须是同类型的结构体变量。

<3>用指向结构体变量(或数组元素)的指针作实参,将结构体变量(或数组元素)的地址传给形参。

5.4 用指针处理链表
链表是一种常见的重要的数据结构。它是动态的进行存储分配的一种结构,它根据需要开辟内存单元。
链表有一个"头指针"变量,以head为例,它存放一个地址,该地址指向一个元素。链表中的每一个元素称为"节点",每个节点都应有两个部分:
<1> 用户所需要的实际数据
<2> 下一节点的位置
链表中各元素在内存中的地址可以是不连续的。要找到某一元素,必须先找到上一个元素,根据它提供的下一元素地址才能找到下一个元素。如果不提供"头指针",则整个链表都无法访问
例如,可以设计这样一个结构体类型:
struct Student
{
int num;
float score;
struct Student * next;
};
其中,成员num和score用来存放节点中的有用数据(用户需要用到的数据)。next是指针类型的成员,它指向struct Student类型数据,这个结构体类型中每一个都属于struct Student类型,它的成员next用来存放下一节点的地址。这个结构体类型并未实际分配存储空间,只有定义了变量才分配存储单元

建立简单的静态链表
所有节点都是在程序中定义,不是临时开辟的,也不能用完后释放,这种链表称为"静态链表"
如:

include <stdio. h>

struct Student //声明结构体类型 struct Student
{int num;
float score;
struct Student * next;
};
int main()
{ struct Student a, b,c, × head, * p; //定义3个结构体变量 a,b,c作为链表的结点
a. num= 10101; a. score=89.5; //对结点a的num 和 score 成员赋值
b. num= 10103; b. score= 90; //对结点6的num 和 score 成员赋值
c. num = 10107; c. score=85; //对结点c的num 和score 成员赋值
head=&.a; //将结点a的起始地址赋给头指针 head
a. next=&b; //将结点b的起始地址赋给a结点的 next 成员
b. next =&.c; //将结点 c的起始地址赋给a结点的 next 成员
c. next = NULL; //结点的 next 成员不存放其他结点地址
p= head; //使p指向a结点
do
{printf(" %ld %5. 1f \n" , p-> num, p-> score); //输出 p指向的结点的数据
p= p-> next; //使p指向下一结点
}while(p!=NULL); //输出完c结点后 p的值为 NULL,循环终止
return 0;
}

建立动态链表
所谓建立动态链表是指在程序执行过程中从无到有建立起一个链表,即一个一个地开辟结点和输入各节点数据,并建立起前后相链的关系。例:写一函数建立 有3名学生数据的单向动态链表

include <stdio. h>

include <stdlib. h>

define LEN sizeof(struct Student)

struct Student
{long num;
float score;
struct Student * next;
};
int n; //n为全局变量,本文件模块中各两数均可使用它
struct Student * creat(void) //定义函数。此西数返回一个指向链表头的指针
{struct Student * head;
struct Student * pl, * p2;
n=0;
p1 = p2 = (struct Student * ) malloc(LEN) ; //开辟一个新单元
scanf(“% ld, % f” , & p1-> num, & p1-> score) ; //输人第1个学生的学号和成绩
head= NULL;
while(p1-> num! =0)
{n=n十1;
if(n==1)head = pl;
else p2-> next = pl;
p2= pl;
p1 = (struct Student * ) malloc(LEN) ; //开辟动态存储区,把起始地址赋给 p1
scanf(“%ld, %f”, & pl-> num, & p1-> score) ; //输人其他学生的学号和成绩
p2->next=NULL;
return(head);
}
可以写一个main函数,调用这个creat函数:
int main()
{
struct Student * pt;
pt=creat(); //函数返回链表第一个结点的地址
printf(“\nnum:%ld\nscore:%5.lf\n”,pt->num,pt->score);
return 0;
};

6 共用体类型

6.1 什么是共用体类型?
使几个不同的变量共享同一段内存的结构,称为"共用体"类型的结构

定义共用体类型变量的一般形式为:
union 共用体名
{
成员表列
}变量表列;
例如:
union Data
{
int i; //表示不同类型的变量i,ch,f可以存放到同一段存储单元中
char ch;
float f;
}a,b,c; //在声明类型同时定义变量

也可以将类型声明与变量定义分开:
union Data
{
int i;
char ch;
float f;
};
union Data a,b,c;

6.2 引用共用体变量的方式
只有先定义了共用体变量才能引用它,但注意,不能引用共用体变量,而只能引用共用体变量中的成员。如:
a.i,a.ch或printf(“%d”,a.i); printf(“%c”,a.ch);

6.3 共用体类型数据的特点
<1> 同一个内存段可用来存放几种不同类型的成员,但在每一瞬间只能存放其中一个成员,而不是同时存放几个

<2> 可以对共用体变量初始化,但初始化表中只能有一个常量

<3> 共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代

<4> 共用体变量的地址和它的各成员的地址都是同一地址

<5> 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值

<6> 以前的C规定不能把共用体变量作为函数参数,但可以使用指向共用体变量的指针作函数参数,C99允许用共用体变量作为函数参数

<7> 共用体类型可以出现在结构体类型的定义中,也可以定义共用体数组

7 使用枚举类型

如果一个变量只有几种可能的值,则可以定义为枚举类型,所谓"枚举"就是指把可能的值一一列举出来,变量的值只限于列举出来的值的范围内。

声明枚举类型用enum开头。例如:
enum Weekday{sun,mon,yue,wed,thu,fri,sat};
workday和weekend被定义为枚举变量,花括号中的称为枚举元素或枚举变量。它们是用户指定的名字。枚举变量和其他数值型量不同,它们的值只限于花括号中指定的值之一。例如枚举变量workday和weekday的值只能是sun到sat之一。

枚举常量是由程序设计者命名的,用什么名字代表什么含义,完全由程序员根据自己的需要而定,并在程序中相应处理
也可以不声明有名字的枚举类型,而直接定义枚举变量,例如:
enum{sun,mon,yue,wed,thu,fri,sat} workday,weekend;

声明枚举类型的一般形式为:enum[枚举名]{枚举元素列表};
其中,枚举名应遵循标识符的命名规则,上面的Weekday就是合法的枚举名
注:
1 C编译对枚举类型的枚举元素按常量处理,故称枚举常量,不要因为它们是标识符而把它们看作变量,不能对它们赋值

2 每一枚举元素都代表一个整数,C语言编译按定义时的顺序默认它们的值为0,1,2,3,4,5…。在上面的定义中,sun的值为1,mon的值为1,…,sat的值为6.如果有赋值语句:
workday=mon; 相当于 workday=1;
枚举常量是可以引用和输出的。例如:
printf(“%d”,workday); 将输出整数1
也可以人为地指定枚举元素地数值,在定义枚举类型时显式地指定,例如:
enum Weekday{sun=7 ,mon=1,tue,wed,thu,fri,sat}workday,week_end;
由于枚举变量地值是整数,因此C99把枚举类型也作为整型数据中的一种,即用户自行定义的整数类型

3 枚举元素可以用来作判断比较。例如:
if (workday==mon)…

8 用typedef声明新类型名

1 简单地用一个新的类型名代替原有的类型名
除了可以直接使用C提供的标准类型名和程序编写者自己声明的结构体,共用体,枚举类型外,还可以用typedef指定新的类型名来代替已有的类型名。例如:
typedef int Integer //使用Integer为类型名,作用与int相同
所以以下两式等价 int i,j; == Integer i,j;

2 命名一个简单的类型名代替复杂的类型表示方法
2.1 命名一个新的类型名代表结构体类型
typedef struct
{
int month;
int day;
int year;
}Data;
以上声明了一个新类型名Data,代表上面的一个结构体类型。可以用新的类型名Data去定义变量,ru:
Data birthday; //定义结构体类型变量birthday

2.2 可命名一个新的类型名代表数组类型
typedef int Num[100]; //声明Num为整型数组类型名
Num a; //定义a为整型数组名,它有100个元素

2.3 可命名一个新的类型名代表指针类型
typedef char * String; //声明String为字符指针类型
String p,s[10]; //定义p为字符指针变量,s为字符指针数组

2.4 可命名一个新的类型名代表指向函数的指针类型
typedef int (*Pointer)(); //声明Pointer为指向函数的指针类型,该函数返回整型值
Pointer p1,p2; //p1,p2为Pointer类型的指针变量

总结:按定义变量的方式,把变量名换上新类型名,并且在最前面加typedef,就声明了新类型名代表原来的类型

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值