C语言,课程以外需补充的内容.doc
《C语言,课程以外需补充的内容.doc》由会员分享,可在线阅读,更多相关《C语言,课程以外需补充的内容.doc(16页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。
1、一、 。以下内容是课程中未涉及的部分,但在等级考试中经常出现的1、 关于有参宏的内容及其与函数的区别(1) 宏定义和宏替换的定义宏定义是将一个标识符(宏名)定义为某个字符串(宏体)。宏定义有不带参数和带参数两种形式: #define 标识符 字符串 #define 标识符(参数表) 字符串宏替换是用已定义的宏体(字符串)来替换宏名(标识符)。其形式: 宏名 宏名(参数表)宏定义和宏替换两者的关系:必须先有宏定义,才能再有宏替换,后者是前者的逆操作。例: #define A(x) x*x main() int a=5; printf(“%dn”,A(a-1); 程序经替换后的printf语句为:
2、 printf(“%dn”,a-1*a-1);替换后再编译运行,程序的结果应为-1。注意事项: 宏定义不是C语句,行末不能有分号。 进行宏替换时,要记住只是简单的代换,不能随意增加或减少内容。 如上例,程序经替换后的printf语句不应该替换为: printf(%dn,(a-1)*(a-1); 如果需要上述的替换,则应将宏定义修改为: #define A(x) (x)*(x) 定义带参数的宏定义时,要特别注意括号的正确使用。(2). 带参数的宏替换与函数调用的区别l 实现的时间不同。宏替换的实现是在编译前完成;而函数调用是在运行时处理的。l 实现的方法不同。宏替换只是简单的代换,即用已定义的宏
3、体来替换宏名,它没有控制的转移。而函数调用是将实参传递给形参,然后转去执行调用的函数体,执行完后,返回到调用的地方继续往下执行。l 宏定义不存在类型问题,即宏名和参数都是无类型的;而函数的形参和实参必须指出类型,且要求形参和实参的类型要相一致。例:运行结果:F1(1+1)=3 F2(1+1)=4#define F1(x) x*x int f2(int x) return x*x; main() int x=1; printf(F1(%d+1)=%dn,x,F1(x+1); printf(f2(%d+1)=%dn,x,f2(x+1); 2、 有关链表的操作准备:动态内存分配一、为什么用动态内存分
4、配但我们未学习链表的时候,如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。比如说我们要存储一个班级学生的某科分数,总是定义一个float型(存在0.5分)数组:float score30;但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道该班级的学生的人数,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。即使你知道该班级的学生数,但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法
5、称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。那么有没有其它的方法来解决这样的外呢体呢?有,那就是动态内存分配。所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点:1、不需要预先分配存储空间;2、分配的空间可以根据
6、程序的需要扩大或缩小。二、如何实现动态内存分配及其管理要实现根据程序的需要动态分配存储空间,就必须用到以下几个函数1、malloc函数malloc函数的原型为:void *malloc (unsigned int size)其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作。下例是一个动态分配的程序:#include #include main()int c
7、ount,*array; /*count是一个计数器,array是一个整型指针,也可以理解为指向一个整型数组的首地址*/if(array(int *) malloc(10*sizeof(int)=NULL)printf(不能成功分配存储空间。);exit(1);for (count=0;count10;count+) /*给数组赋值*/arraycount=count;for(count=0;count10;count+) /*打印数组元素*/printf(%2d,arraycount);上例中动态分配了10个整型存储区域,然后进行赋值并打印。例中if(array(int *) malloc(
8、10*sizeof(int)=NULL)语句可以分为以下几步:1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型指针2)把此整型指针地址赋给array3)检测返回值是否为NULL2、free函数由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。这时我们就要用到free函数。其函数原型是:void free(void *p)作用是释放指针p所指向的内存区。其参数p必须是先前调用malloc函数或calloc函数(另一个动态分配存储区域的函数)时返回的指针。给free函数传递其它的值很可能造
9、成死机或其它灾难性的后果。注意:这里重要的是指针的值,而不是用来申请动态内存的指针本身。例:int *p1,*p2;p1=malloc(10*sizeof(int);p2=p1;free(p2) /*或者free(p2)*/malloc返回值赋给p1,又把p1的值赋给p2,所以此时p1,p2都可作为free函数的参数。malloc函数是对存储区域进行分配的。free函数是释放已经不用的内存区域的。所以由这两个函数就可以实现对内存区域进行动态分配并进行简单的管理了。一、单链表的建立有了动态内存分配的基础,要实现链表就不难了。所谓链表,就是用一组任意的存储单元存储线性表元素的一种数据结构。链表又分
10、为单链表、双向链表和循环链表等。我们先讲讲单链表。所谓单链表,是指数据接点是单向排列的。一个单链表结点,其结构类型分为两部分:1、数据域:用来存储本身数据2、链域或称为指针域:用来存储下一个结点地址或者说指向其直接后继的指针。例:typedef struct nodechar name20;struct node *link;stud;这样就定义了一个单链表的结构,其中char name20是一个用来存储姓名的字符型数组,指针*link是一个用来存储其直接后继的指针。定义好了链表的结构之后,只要在程序运行的时候爱数据域中存储适当的数据,如有后继结点,则把链域指向其直接后继,若没有,则置为NUL
11、L。下面就来看一个建立带表头(若未说明,以下所指链表均带表头)的单链表的完整程序。#include #include /*包含动态内存分配函数的头文件*/#define N 10 /*N为人数*/ typedef struct nodechar name20;struct node *link;stud; stud * creat(int n) /*建立单链表的函数,形参n为人数*/stud *p,*h,*s; /* *h保存表头结点的指针,*p指向当前结点的前一个结点,*s指向当前结点*/int i; /*计数器*/if(h=(stud *)malloc(sizeof(stud)=NULL)
12、 /*分配空间并检测*/printf(不能分配内存空间!);exit(0);h-name0=0; /*把表头结点的数据域置空*/h-link=NULL; /*把表头结点的链域置空*/p=h; /*p指向表头结点*/for(i=0;ilink=s; /*把s的地址赋给p所指向的结点的链域,这样就把p和s所指向的结点连接起来了*/printf(请输入第%d个人的姓名,i+1);scanf(%s,s-name); /*在当前结点s的数据域中存储姓名*/s-link=NULL;p=s;return(h);main()int number; /*保存人数的变量*/stud *head; /*head是保
13、存单链表的表头结点地址的指针*/number=N;head=creat(number); /*把所新建的单链表表头地址赋给head*/这样就写好了一个可以建立包含N个人姓名的单链表了。写动态内存分配的程序应注意,请尽量对分配是否成功进行检测。 二、单链表的基本运算建立了一个单链表之后,如果要进行一些如插入、删除等操作该怎么办?所以还须掌握一些单链表的基本算法,来实现这些操作。单链表的基本运算包括:查找、插入和删除。下面我们就一一介绍这三种基本运算的算法,并结合我们建立单链表的例子写出相应的程序。1、查找对单链表进行查找的思路为:对单链表的结点依次扫描,检测其数据域是否是我们所要查好的值,若是返
14、回该结点的指针,否则返回NULL。因为在单链表的链域中包含了后继结点的存储地址,所以当我们实现的时候,只要知道该单链表的头指针,即可依次对每个结点的数据域进行检测。 以下是应用查找算法的一个例子:#include #include #include /*包含一些字符串处理函数的头文件*/#define N 10typedef struct nodechar name20;struct node *link;stud;stud * creat(int n) /*建立链表的函数*/stud *p,*h,*s;int i;if(h=(stud *)malloc(sizeof(stud)=NULL)p
15、rintf(不能分配内存空间!);exit(0);h-name0=0;h-link=NULL;p=h;for(i=0;ilink=s;printf(请输入第%d个人的姓名,i+1);scanf(%s,s-name);s-link=NULL;p=s;return(h);stud * search(stud *h,char *x) /*查找链表的函数,其中h指针是链表的表头指针,x指针是要查找的人的姓名*/stud *p; /*当前指针,指向要与所查找的姓名比较的结点*/char *y; /*保存结点数据域内姓名的指针*/p=h-link;while(p!=NULL)y=p-name;if(str
16、cmp(y,x)=0) /*把数据域里的姓名与所要查找的姓名比较,若相同则返回0,即条件成立*/return(p); /*返回与所要查找结点的地址*/else p=p-link;if(p=NULL)printf(没有查找到该数据!);main()int number;char fullname20;stud *head,*searchpoint; /*head是表头指针,searchpoint是保存符合条件的结点地址的指针*/number=N;head=creat(number);printf(请输入你要查找的人的姓名:);scanf(%s,fullname);searchpoint=sear
17、ch(head,fullname); /*调用查找函数,并把结果赋给searchpoint指针*/ 2、插入(后插)假设在一个单链表中存在2个连续结点p、q(其中p为q的直接前驱),若我们需要在p、q之间插入一个新结点s,那么我们必须先为s分配空间并赋值,然后使p的链域存储s的地址,s的链域存储q的地址即可。(p-link=s;s-link=q),这样就完成了插入操作。下例是应用插入算法的一个例子:#include #include #include #define N 10typedef struct nodechar name20;struct node *link;stud;stud *
18、 creat(int n) /*建立单链表的函数*/stud *p,*h,*s;int i;if(h=(stud *)malloc(sizeof(stud)=NULL)printf(不能分配内存空间!);exit(0);h-name0=0;h-link=NULL;p=h;for(i=0;ilink=s;printf(请输入第%d个人的姓名:,i+1);scanf(%s,s-name);s-link=NULL;p=s;return(h);stud * search(stud *h,char *x) /*查找函数*/stud *p;char *y;p=h-link;while(p!=NULL)y=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 课程 以外 补充 内容
限制150内