资源预览内容
第1页 / 共20页
第2页 / 共20页
第3页 / 共20页
第4页 / 共20页
第5页 / 共20页
第6页 / 共20页
第7页 / 共20页
第8页 / 共20页
第9页 / 共20页
第10页 / 共20页
亲,该文档总共20页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
1.1.指针函数的定义指针函数的定义顾名思义,指针函数即返回指针的函数。其一般定义形式如下:类型名类型名 *函数名函数名(函数参数表列函数参数表列);其中,后缀运算符括号“()”表示这是一个函数,其前缀运算符星号“*” 表示此函数为指针型函数,其函数值为指针,即它带回来的值的类型为指针, 当调用这个函数后,将得到一个“指向返回值为的指针(地址),“类型名类型名” 表示函数返回的指针指向的类型”。 “(函数参数表列函数参数表列)”中的括号为函数调用运算符,在调用语句中,即使函 数不带参数,其参数表的一对括号也不能省略。其示例如下:int *pfun(int, int);由于“*”的优先级低于“()”的优先级,因而 pfun 首先和后面的“()” 结合,也就意味着,pfun 是一个函数。即:int *(pfun(int, int);接着再和前面的“*”结合,说明这个函数的返回值是一个指针。由于前面 还有一个 int,也就是说,pfun 是一个返回值为整型指针的函数。 我们不妨来再看一看,指针函数与函数指针有什么区别?int (*pfun)(int, int);通过括号强行将 pfun 首先与“*”结合,也就意味着,pfun 是一个指针, 接着与后面的“()”结合,说明该指针指向的是一个函数,然后再与前面的 int 结合,也就是说,该函数的返回值是 int。由此可见,pfun 是一个指向返 回值为 int 的函数的指针。虽然它们只有一个括号的差别,但是表示的意义却截然不同。函数指针的 本身是一个指针,指针指向的是一个函数。指针函数的本身是一个函数,其函 数的返回值是一个指针。2 2. . 用函数指针作为函数的用函数指针作为函数的 r 返回值返回值在上面提到的指针函数里面,有这样一类函数,它们也返回指针型数据 (地址),但是这个指针不是指向 int、char 之类的基本类型,而是指向函数。 对于初学者,别说写出这样的函数声明,就是看到这样的写法也是一头雾水。 比如,下面的语句:int (*ff(int)(int *, int);我们用上面介绍的方法分析一下,ff 首先与后面的“()”结合,即:int (*(ff(int)(int *, int);/ 用括号将 ff(int)再括起来也就意味着,ff 是一个函数。 接着与前面的“*”结合,说明 ff 函数的返回值是一个指针。然后再与后 面的“()”结合,也就是说,该指针指向的是一个函数。 这种写法确实让人非常难懂,以至于一些初学者产生误解,认为写出别人 看不懂的代码才能显示自己水平高。而事实上恰好相反,能否写出通俗易懂的 代码是衡量程序员是否优秀的标准。一般来说,用 typedef 关键字会使该声明 更简单易懂。在前面我们已经见过:int (*PF)(int *, int);也就是说,PF 是一个函数指针“变量变量”。当使用 typedef 声明后,则 PF 就成 为了一个函数指针“类型类型”,即:typedef int (*PF)(int *, int);这样就定义了返回值的类型。然后,再用 PF 作为返回值来声明函数:PF ff(int);下面将以程序清单 1 为例,说明用函数指针作为函数的返回值的用法。当 程序接收用户输入时,如果用户输入 d,则求数组的最大值,如果输入 x,则求 数组的最小值,如果输入 p,则求数组的平均值。程序清单 1 求最值与平均值示例1 #include2 #include3 double GetMin(double *dbData, int iSize) / 求最小 值4 5double dbMin;6int i;78assert(iSize0);9dbMin=dbData0;10for (i=1; idbDatai) 12 dbMin=dbDatai;13 1415return dbMin;16 1718 double GetMax(double *dbData, int iSize) / 求最 大值19 20double dbMax;21int i;2223assert(iSize0);24dbMax=dbData0;25for (i=1; i0);39for (i=0; i符,计算 机才认为用户数据输入完毕。而包括回车符在内的数据总得有个落脚点吧!当然,聪明的 计算机设计大师绝不会让用户输入的有价值的数据无缘无故地消失掉,他们早已考虑了这 些细节问题,事先为这些数据的去处作了充分周密的安排。与极速运行的 CPU 相比,数据的输入/输出和键盘的 I/O 接口是一个极其缓慢的外设。 为了减少 CPU 的等待时间,因此在内存中开辟了一个叫做“缓冲区”的存储单元,用于保 存输入或输出数据。“缓冲区”其实相当于一个仓库,其示意图详见图 4.2(略)。 打一个比方,“通过键盘输入数据”好比正在生产产品的工厂,“缓冲区” 就像仓库,用于放置已经生产出来的产品,而“读取数据”则好比卖东西的门 市部。我们想一想,如果没有仓库,则工厂生产出来的产品就没地方放置,必 须生产一个产品,门市部再去取一个产品,然后再生产再取,如果生产 10 个产 品就得来回跑 10 趟,这样岂不是很浪费资源?于是就有了仓库,工厂不再需要 直接和门市部打交道,它只需要将生产好的产品放到仓库就行,而门市部也不 需要立即去处理已经生产的产品,当要用的时候再去仓库取就好了。由此可见,从键盘输入数据存放到缓冲区,即相当于工厂将生产好的产品放 到仓库中。而从缓冲区读取数据,即相当于从仓库取走产品。当 scanf 函数检测到n符时,则立即从缓冲区中取走输入数据赋值 给变量,但却不会取走对用户来说无效的n符。虽然n符用 作输入数据结束的标志符,但也可作为每一行源代码结束的标志符。此时,即使用户第二次执行读取数据操作,而计算机则会直接跳过去,因为 缓冲区内只有n符。其实这些可能的不合情理的操作,计算机设计大 师犹如神机妙算早已料到,每次都会让计算机检测缓冲区是非为空,只有缓冲 区不空时,才允许执行读取数据操作。2. 变量的地址变量的地址虽然“缓冲区”这个名字对于初学者来说是第一次接触,但实际上它仅仅是 一个“中转站”而已,因此也不需要知道保存这些数据的存储单元地址。而变 量则不一样,它在内存中必须具有确定的位置,否则无法一直保存以备再次使 用。那么到底如何获得变量的地址呢?C 语言用“定义了一个整型值变量 num 并初始化为 1。通过前面的学习,我们知道,num 是变量名,变量 num 的值为 1,那么保存 数值 1 的变量的地址就是“即就是将从键盘输入的数据存放到缓冲区,当检测到n符时,则立即 从缓冲区读取数据赋值给变量 num。3输入函数格式输入函数格式用 scanf 函数输入数据的一般形式如下:scanf(格式控制,地址表列);“格式控制”与 printf 函数完全相同,它同样包括“格式声明和普通字符” 信息。而“地址表列”则是由若干个地址所组成的表列,因此既可为变量的地 址,也可为字符串的首地址控制台菜单选项的实现(控制台菜单选项的实现(2)入口检查入口检查(2010-09-27 00:00:09) 转载 标签: 入口检查字符变量测试用例教育声明:在这一个系列中本着一题多解,步步深入以及尽量降低初学者的阅读 难度减少枝叶代码的精神,很多细节都没有处理。例如举的例子中的函数的返 回值都是 void,函数中间没有安全检查,及其它的一些问题,通过例子中的几 十行代码不可能做的面面俱到,在实际编程中是不能这样的,请同学们不要盲 目效仿。在 V0.1 版本中,我们提到了输入参数的合法性检查问题,不过还是先介绍 另一个非常重要的理念“健壮性”。在大多数本科教材中也只是提到了这 个概念,却并没有深入的去探讨,其实不然,对于一个合格的程序员来说,软 件的健壮性理念必须放在首位。很多到企业实习的学生编写的代码,一般都能 够正确地实现程序的功能,一些学生甚至还会做到功能上的扩展,但是所有的 学生都有一个共同的毛病,从来不做异常处理,只要用户输入不合法,则程序 很容易崩溃,这是不允许发生的,也是用户绝对不能容忍的。虽然软件在发布 前必须经过大量的测试,但希望初学者必须养成良好的编程习惯。下面开始讨论 V0.1 版本软件接受用户输入的合法性判断。为什么要重点判 断这个问题呢?道理很简单:“用户都是傻瓜”,当然,我们不是说使用者智 商有任何问题,而是开发人员应该这么去想。开发者无法预知用户会输入那些 奇奇怪怪的内容,但是好的程序都必须能够处理那些看来莫名其妙的问题。我 们到底将如何解决这样的问题呢?1方法方法 1“scanf(“%d“,”语句是 V0.1 版本出现死循环的关键,那么我们不妨先从这里入手。其实只要将“%d“改为“%c”,即可解决死循环问题。 比如:scanf(“%c“, / chCmd 是一个字符变 量iCmdNum = atoi( / 将 chCmd 转 换为整型数值为什么这样做就能够解决死循环问题呢?我们不妨编写不同的测试用例(在 这里仅仅需要输入不同的数值即可)试试。如果出现问题,将如何解释? 提示提示: 控制台中的所有输入都被认为是字符; 回车键是一个或两个字符(不同的操作系统和编译器有不同的解释, 而在 Windows 控制台中回车键的 ASCII 码为 10); 查看 atoi 的使用方法,注意它执行失败后的返回值。为什么将“%d“改为“%c”即可避免死循环?大家知道 V0.1 版本出现死 循环的原因完全是因为 scanf 从缓冲区读取值失败所引起的。因为在输入输出 流中所有的类型都会被当作字符来看待,那么当使用%c 来读入 chCmd 时,不 管输入什么样的值都会成功。当 scanf 成功之后,则会将缓冲区中的字符清空 (实际上是将“流指针”后移),等待下一次用户的输入,所以它不会出现死循 环。2方法方法 2既然问题是由用户输入函数带来的,那么我们即可使用其它标准库函数来 解决这个问题。根据控制台菜单程序用户输入的特点,我们不妨输入单字符来 触发功能,即通过 getchar 函数来实现用户交互。但是,我们一定要注意其中 的细节,该函数的原型int getchar(void)在常见的 c 编译器里面,char 类型取值范围为-128, 127,类似 EOF 等宏 的值则在此取值范围之外,那么 EOF 将不会被正确地读入,那么char c= getchar();则是一个隐藏很深的 bug。在这里我们将接受输入值的变量定义为 int。程序清单 2 用 if else 做分支处理并用字符作比较(V0.2)void CmdRunning()int iCmdNum=0;do printf(“请选择:0. 退出;1. 新建文件;2. 打开文件;3. 保存文件n“);iCmdNum = getchar();fflush(stdin); / 清空缓冲区if (0 = iCmdNum) printf(“谢谢使用,再见!n“);else if (1 = iCmdNum) CreateFile();else if (2 = iCmdNum) OpenFile();else if (3 = iCmdNum) SaveFile();else printf(“对不起,你选择的数字不存在,请重新选择
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号