全能网站建设教程,有没有做彩票直播的网站,企业网站建设市场分析,保健品网站建设策划书12.1从基本数据类型到抽象数据类型
1 所有的程序设计语言都不能将所有复杂数据对象作为其基本数据类型#xff0c;因此需要允许用户自定义数据类型#xff0c;在C语言中#xff0c;就存在构造数据类型#xff08;复合数据类型#xff09;。 2 结构体是构造数据类型的一种…12.1从基本数据类型到抽象数据类型
1 所有的程序设计语言都不能将所有复杂数据对象作为其基本数据类型因此需要允许用户自定义数据类型在C语言中就存在构造数据类型复合数据类型。 2 结构体是构造数据类型的一种经典代表。 3 抽象数据类型是不再单纯是一组值的集合还包括作用在值集上操作的集合且类型的表示细节和操作的实现对外是不可见的。 4 C中的类是抽象数据的一种具体实现也是面向对象程序设计语言中的一个重要概念但是不能将C看成是带类的C。 12.2 结构体的定义
1 数组的存储方式存在一些问题如(1)分配内存不集中寻址效率低2对数组赋初值时容易发生错位3结构显得零散不易管理。 2 数组适合对具有相同属性的数据进行批处理而结构体将不同类型的数据成员组织到统一的名字下共用体虽然也能表示逻辑相关的不同类型的集合但是数据成员是情景互斥的。 3 结构体模板的格式如下 注意结构体模板并未声明结构体类型的变量因此不会被分配内存。 4 C语言有两种方式定义结构体变量. 5 关键字typedef可用于为系统固有的或自定义的数据类型定义一个别名它只是定义一个新的名字而不是定义一种新的数据类型。 6 结构体变量的初始化格式
结构体类型 结构体变量{值1值2……值n}; 7 结构体可以实现嵌套即在一个结构体中包含了另一个结构体作为其成员。 8 访问结构体变量的成员必须使用成员选择运算符也称圆点运算符其格式为
结构体变量名 . 成员名 9 出现结构体嵌套时必须以级联方式访问结构体成员也就是通过成员选择运算符逐级找到最底层的成员再引用。
例12.1见文末 10 C语言允许对具有相同结构体类型的变量进行整体赋值如stu1stu2 11 并非所有的结构体成员都可以用赋值运算符来赋值对字符数组类型的结构体成员进行赋值时必须使用字符串处理函数strcpy()。 12 结构体类型的声明既可以放在所有函数体的外部全局声明可以被所有函数使用也可以放在函数体的内部局部声明只能再本函数体内使用。 13 结构体变量的地址是结构体变量所占内存空间的首地址而结构体成员的地址值与结构体成员再结构体中所处的位置以及该成员所占内存的字节数有关。
例122见文末 14 系统为结构体变量分配内存的大小或者说结构体类型所占内存的字节数并非是所有成员所占内存字节数的总和它不仅与所定义的结构体类型有关还与计算机系统本身有关。 12.3 结构体数组的定义和初始化
1 结构体数组的定义格式为
结构体类型 结构体变量[数量] 2 可以在定义时初始化结构体数组否则其他数组元素被系统自动赋值为0。
例12.3见文末 12.4 结构体指针的定义和初始化
1 如果已经声明了STUDENT的结构体类型那么指向该结构类型的指针变量的方法为
STUDENT *pt; //指针变量pt的值是随机值
为了使pt 指向确定的存储单元需要对指针变量进行初始化如
ptstu1;
以上两条语句等价于
STUDENT *ptstu1; 2 访问指针变量指向的结构体成员有两种方式一使用成员选择符二使用指向运算符也称箭头运算符。 3 使用箭头运算符访问结构体成员的标准形式为
pt-studentID1003101221;
等价于
*pt.studentID1002101221;//不常用 4 访问嵌套结构体指针变量下的成员通常结合成员选择符和指向运算符如
pt-birthday.year2002; 12.5 向函数传递结构体
1 向函数体传递结构体有三种方法。 2 方法一用结构体的单个成员作为函数参数向函数传递结构体的单个成员。这种方法与普通类型的变量作为函数实参没有区别都是按值调用。 3 方法二用结构体变量作函数参数向函数传递结构体的完整结构。通过这种方法可以在函数内用成员选择运算符引用结构体其他成员同样是按值传递但是时空开销大并且只能当行参和实参是同一结构体才可以使用。 4 方法三用结构体指针或结构体数组作函数参数向函数传递结构体的地址。这种方法属于传地址调用效率更高。
例12.4见文末 5 使用方法一和方法二结构体变量的成员值是不能在被调函数中被修改而方法三可以被修改。
例12.5~例12.7见文末 12.6 共用体
1 共用体也称为联合是将不同类型的数据组织在一起共同占有同一段内存的一种构造数据类型。 2 共用体与结构体的声明方法类似只是关键字变成union。
例12.8见文末 3 共用体所占的内存空间大小取决于成员中占内存空间最多的那个成员变量。 4 共用体采用与开始地址对齐的方式分配内存空间。 5 共用体通过覆盖技术来实现内存的共用因此在每一瞬起作用的成员是最后一次被赋值的成员。 6 不能为共用体的所有成员同时初始化。 7 共用体不能进行比较操作也不能作为函数参数。 12.7 枚举数据类型
1 当某些量仅由有限个数据值组成时通常用枚举类型来表示。 2 枚举描述的是一组整型值的集合需要使用关键字enum来定义。 3 枚举类型的声明格式为
enum 枚举标签{枚举常量1枚举常量2……枚举常量n}; 4 枚举常量的定义格式为
enum 枚举标签 枚举变量名;
可以将枚举常量赋值给对应的枚举标签。 5 当枚举标签和枚举变量放在一起定义时枚举标签可以省略不写如
enum {no,yes,none} answer; 6 虽然枚举标签后面的花括号内的标识符代表枚举型变量的可能取值但其值是整型常数不是字符串因此不能作为字符串来使用。 12.8 动态数据结构—单向链表
1 动态数据结构是利用动态内存分配使用结构体并且配合指针来实现的一种数据结构。 2 在结构体声明时不能包含结构体类型成员但可以包含指向本结构体类型的指针成员。 3 链表实际是链式存储、顺序访问的线性表它是用一组任意的存储单元来存储线性表中的数据存储单元不一定是连续的链表长度是不固定的。 4 链表的每一个元素称为一个节点节点包含数据域和指针域两部分。 5 节点的数据域用于存储本身的数据信息节点的指针域是一个结构体指针用于存储其直接后继的节点信息当其值为空指针NULL时表示链表结束。 6 链表中还需有一个指向链表的起始节点的头指针变量。 7 只包含一个指针域由n个节点链接形成的链表就被称为线性链表或者单向链表。 8 链表只能顺序访问不能随机访问因此最大的缺点是容易出现断链且断链后影响大。 9 单向链表的建立考虑1若原链表为空表则将新建节点置为头结点;(2)若原链表非空,则将新建节点添加到表尾.
例12.9见文末 10 单向链表的删除考虑 (1)若原链表为空表,则无须删除,直接退出程序;(2)若找到的待删除节点是头节点,则将头指针变量指向当前节点的下一个节点;(3)若找到的待删除节点不是头结点,则将前一节点的指针域指向当前节点的下一节点;(4)搜索到表尾,依旧未找到待删除节点,则提示未找到.
例12.10见文末 11 单向链表的插入考虑 (1)若原链表为空表,则将新节点作为头结点;(2)若原链表非空,则按节点值的大小确定插入新节点;(3)若在表尾插入新节点,则末节点指针域指向新节点.
例12.11见文末 代码
12.1a
演示结构体变量的赋值与用于方法,从键盘输入数据
/12.1a演示结构体变量的赋值与用于方法
#includestdio.h
typedef struct date
{int year;int month;int day;}DATE;//别名为DATEtypedef struct student{long studentID;char studentName[10];char studentSex;DATE birthday;//嵌套结构体 int score[4]; }STUDENT;
int main(void)
{STUDENT stu1{100210121,王刚,M,{1991,5,19},{72,83,90,82}};//初始化,赋值用花括号 STUDENT stu2;//定义一个相同结构体类型的结构体变量stu2stu2stu1;//相同结构体类型之间进行赋值printf(stu2:%10ld%8s%3c%6d/%02d/%02d%4d%4d%4d%4d\n,//这里的%02d表示输出数据时若左边有多余位则补0stu2.studentID,stu2.studentName,stu2.studentSex,//对应学号、姓名、性别 stu2.birthday.year,stu2.birthday.month,stu2.birthday.day,//对应出生年月日stu2.score[0], stu2.score[1], stu2.score[2], stu2.score[3]); return 0;}
12.1b
演示结构体变量的赋值与用于方法,从键盘输入数据 //12.1b演示结构体变量的赋值与用于方法,从键盘输入数据
#includestdio.h
typedef struct date
{int year;int month;int day;}DATE;//别名为DATEtypedef struct student{long studentID;char studentName[10];char studentSex;DATE birthday;//嵌套结构体 int score[4]; }STUDENT;
int main(void)
{STUDENT stu1,stu2; //定义结构体变量int i;//用于控制成绩输入printf(Input a recoed:\n);scanf(%ld,stu1.studentID);scanf(%s,stu1.studentName);scanf( %c,stu1.studentSex);//%c前面有一个空格 scanf(%d,stu1.birthday.year);scanf(%d,stu1.birthday.month);scanf(%d,stu1.birthday.day);for(i0;i4;i){scanf(%d,stu1.score[i]);}stu2stu1;//相同结构体类型之间进行赋值printf(stu2%p\n,stu2);//打印stu2的地址 printf(stu2:%10ld%8s%3c%6d/%02d/%02d%4d%4d%4d%4d\n,//这里的%02d表示输出数据时若左边有多余位则补0stu2.studentID,stu2.studentName,stu2.studentSex,//对应学号、姓名、性别 stu2.birthday.year,stu2.birthday.month,stu2.birthday.day,//对应出生年月日stu2.score[0], stu2.score[1], stu2.score[2], stu2.score[3]); return 0;}
12.2
演示结构体所占字节数的计算方法
//12.2 演示结构体所占字节数的计算方法
#includestdio.h
typedef struct sample
{char m1;int m2;char m3;
}SAMPLE;
int main(void)
{SAMPLE s{a,2,b};printf(bytes%d\n,sizeof(s));return 0;}
12.3
利用结构体数组计算每个学生的4门课程的平均分
//例12.3 利用结构体数组计算每个学生的4门课程的平均分
#includestdio.h
typedef struct date
{int year;int month;int day;}DATE;//别名为DATEtypedef struct student{long studentID;char studentName[10];char studentSex;DATE birthday;//嵌套结构体 int score[4]; }STUDENT;
int main(void)
{int i,j,sum[30]; STUDENT stu[30]{{100210121,张三,M,{1991,5,19},{72,93,80,52}},{100210122,李四,F,{1990,3,22},{83,93,73,12}},{100210123,王二,F,{1991,11,9},{92,73,93,86}},{100210124,赵五,M,{1991,5,5},{92,84,95,86}}};//注意花括号的对应for(i0;i4;i){sum[i]0;for(j0;j4;j){sum[i]sum[i]stu[i].score[j];}printf(%10ld%8s%3c%6d/%02d/%02d%4d%4d%4d%4d%6.1f\n,stu[i].studentID,stu[i].studentName,stu[i].studentSex,//对应学号、姓名、性别 stu[i].birthday.year,stu[i].birthday.month,stu[i].birthday.day,//对应出生年月日stu[i].score[0], stu[i].score[1], stu[i].score[2], stu[i].score[3],sum[i]/4.0); } return 0;}
12.4
演示结构体变量作函数实参实现传值调用
//12.4 演示结构体变量作函数实参实现传值调用
#includestdio.h
struct date
{int year;int month;int day;
};//无别名不要忘记分号
void Func(struct date p)//结构体变量作函数形参
{p.year2000;p.month5;p.day22;}
int main(void)
{struct date d;d.year1999;d.month4;d.day23;printf(Before function call:%d/%02d/%02d\n,d.year,d.month,d.day);Func(d);printf(After function call:%d/%02d/%02d\n,d.year,d.month,d.day);return 0;
}
12.5
修改12.4改用结构体指针变量作函数实参
//12.5 修改12.4改用结构体指针变量作函数实参
#includestdio.h
struct date
{int year;int month;int day;
};//无别名不要忘记分号
void Func(struct date *pt)//结构体zz变量作函数形参
{pt-year2000;//指针运算符 pt-month5;pt-day22;}
int main(void)
{struct date d;d.year1999;d.month4;d.day23;printf(Before function call:%d/%02d/%02d\n,d.year,d.month,d.day);Func(d);//传地址调用 printf(After function call:%d/%02d/%02d\n,d.year,d.month,d.day);return 0;
}
12.6
从函数返回结构体变量的值
//12.6 从函数返回结构体变量的值
#includestdio.h
struct date
{int year;int month;int day;
};
struct date Func(struct date p)//返回类型是struct date型
{p.year2000;p.month5;p.day22;return p;}
int main(void)
{struct date d;d.year1999;d.month4;d.day23;printf(Before function call:%d/%02d/%02d\n,d.year,d.month,d.day);dFunc(d);printf(After function call:%d/%02d/%02d\n,d.year,d.month,d.day);return 0;
}
12.7
修改12.3程序用结构体数组作函数参数编程并输出计算学生的平均分
//例12.7 修改12.3程序用结构体数组作函数参数编程并输出计算学生的平均分
#includestdio.h
#define N 30
typedef struct date
{int year;int month;int day;}DATE;//别名为DATEtypedef struct student{long studentID;char studentName[10];char studentSex;DATE birthday;//嵌套结构体 int score[4]; }STUDENT;
void InputScore(STUDENT stu[],int n,int m);
void AverScore(STUDENT stu[],float aver[],int n,int m);
void PrintScore(STUDENT stu[],float aver[],int n,int m);
int main(void)
{float aver[N];STUDENT stu[N];int n;printf(How many students?);scanf(%d,n);InputScore(stu,n,4);AverScore(stu,aver,n,4);PrintScore(stu,aver,n,4);return 0;}
void InputScore(STUDENT stu[],int n,int m){int i,j;for(i0;in;i){printf(Input recore %d:\n,i1);scanf(%ld,stu[i].studentID);scanf(%s,stu[i].studentName);//这里无取地址符scanf( %c,stu[i].studentSex);scanf(%d,stu[i].birthday.year);scanf(%d,stu[i].birthday.month);scanf(%d,stu[i].birthday.day);for(j0;jm;j){scanf(%d,stu[i].score[j]);}}
}
void AverScore(STUDENT stu[],float aver[],int n,int m){int i,j,sum[N];for(i0;in;i){sum[i]0;for(j0;j4;j){sum[i]sum[i]stu[i].score[j]; }aver[i](float)sum[i]/m;}
}
void PrintScore(STUDENT stu[],float aver[],int n,int m){int i,j;printf(Result:\n);for(i0;in;i){printf(%10ld%8s%3c%6d/%02d/%02d,stu[i].studentID,stu[i].studentName,stu[i].studentSex,//对应学号、姓名、性别 stu[i].birthday.year,stu[i].birthday.month,stu[i].birthday.day); //对应出生年月日for(j0;jm;j){printf(%4d,stu[i].score[j]);}printf(%6.1f\n,aver[i]);}
}
12.8
演示共用体所占内存字节数的计算方法
//12.8 演示共用体所占内存字节数的计算方法
#includestdio.h
union sample
{char i;int ch;char f;
};
typedef union sample SAMPLE;
int main(void)
{printf(bytes%d\n,sizeof(SAMPLE));return 0;}
12.9
单向链表的建立
//12.9 单向链表的建立
#includestdio.h
#includestdlib.h
struct link *AppendNode(struct link *head);
void DisplyNode(struct link *head);
void DeleteMemory(struct link *head);
struct link
{int data;struct link *next;
};int main(void)
{int i0;char c;struct link *headNULL;//链表的头指针printf(Do you want to append a new node(Y/N)?);scanf( %c,c);while(cY||cy){headAppendNode(head);//向head为头指针的链表末添加节点DisplyNode(head);//显示当前链表中的各节点信息printf(Do you want to append a new node(Y/N)?);scanf( %c,c);i; } printf(%d new nodes have been apended!\n,i);DeleteMemory(head);//释放所有动态分配的内存 return 0;
}
//函数功能新建一个节点并添加到链表末尾返回添加节点后的链表的头指针
struct link *AppendNode(struct link *head)
{struct link *pNULL,*prhead;int data;p(struct link *)malloc(sizeof(struct link));//p指向新建节点if(pNULL){printf(No enough memory to allocate!\n);//提醒用户分配失败exit(0);//退出程序 } if(headNULL){//原链表为空表 headp;//新建节点置为头结点 }else{while(pr-next!NULL)//未到末尾就移动pr让pr指向表尾{prpr-next;//让pr指向下一个节点 }pr-nextp;//末节点的指针域指向新建节点 }printf(Input node data:);scanf(%d,data);p-datadata;p-nextNULL;//将新建节点置为表尾 return head;} //函数功能显示链表中所有节点的节点号和该节点中的数据项void DisplyNode(struct link *head){struct link *phead;int j1;while(p!NULL)//不是表尾{printf(%5d%10d\n,j,p-data);pp-next;j;} } //函数功能释放head指向的链表中所有节点占用的内存void DeleteMemory(struct link *head){struct link *phead,*prNULL;while(p!NULL){prp;//用pr保存当前节点 pp-next;//移动指针使其指向下一个节点 free(pr);//释放当前节点 }}
12.10
单向链表的删除操作函数
//12.10 单向链表的删除操作函数
//函数功能从head 指向的链表中删除一个节点返回删除节点后的链表的头指针
struct link *DeleteNode(struct link *head,int nodeData)
{struct link *phead,*prhead;if(headNULL)//空表{printf(Linked Table is empty!\n)return (head);} while(nodeData!p-datap-next!NULL)//未找到并且 未到表尾{prp;//pr保存当前节点 pp-next; //指向下一节点 } if(nodeDatap-data)//找到待删除节点{if(phead){headp-next;//如果是头节点就让头结点指向待删除节点的下一个节点 }else{pr-nextp-next;//前一节点的指针域指向待删除节点的下一个节点 }free(p);//释放删除节点的内存 }else //到来表尾依旧没有找到待删除节点{printf(This Node has nor been found!\n);} return head;}
12.11
单向链表的插入操作函数
//12.11单向链表的插入操作函数
//函数功能在已按升序排序的链表中插入一个新节点返回插入节点后的链表的头指针
struct link *InsertNode(struct link *head,int nodeData)
{struct link *prhead,*phead,*tempNULL;p(struct link *)malloc(sizeof(struct link));//p指向待插入节点 if(pNULL){//内存分配失败 printf(No enough memory!\n);exit(0);}p-nextNULL;//指针域赋值为空 p-datanodeData;//赋值数据 if(headNULL){//空表 headp;}else{while(pr-datanodeDatapr-next!NULL){temppr;//用temp保存当前节点的指针 prpr-next;//指向下一节点 }if(pr-datanodeData){if(prhead)//在头结点前插入 {p-nexthead;//将新节点的指针域指向原链表的头节点 headp;//让head指向新节点 }else//在链表之间插入 {prtemp;p-nextpr-next;//新节点的指针域指向下一节点 pr-nextp;//前一节点的指针域指向新节点 }}else//在链表末尾插入 {pr-nextp;//末节点指向新节点 }}return head;}