c语言高级教程第四章.ppt_第1页
c语言高级教程第四章.ppt_第2页
c语言高级教程第四章.ppt_第3页
c语言高级教程第四章.ppt_第4页
c语言高级教程第四章.ppt_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

李堂秋 厦门大学计算机科学编写,C与C+程序设计 第四章 函数和程序结构,前言,使用函数,可以把大的计算任务分解,换句话说,可以使用许多函数的组合来完成复杂的任务。而且在C中,并非所有的函数都要自己定义,可以使用别人已做好的程序模块。在设计主函数的时候,可以将不必要的细节蕴藏起来,以便更容易把握主线和全局。如果程序太大,还可以将C语言写的程序分放多个文件,分别编译,最后再联接起来成为整体。,4.1 函数的基础,看看我们如何编一个程序, 首先要明确我们要做什么事请,比如要读一个文件,找出所有包含某一字符串的行并把它打印出来。 思路: while (读入一个新行) if (如果这个新行包含指定的字符串) 打印这个新行,读入一个新行,打印这一行,判断这一行是否指定的字符串,函数的基础(续), () 除了和()、 其他都可以省略,如: dummy( ) ;,数据传入,数据通过 return ; 传回给调用函数,#include #define MAXLINE 1000 int getline(char line , int max); int strindex(char source , char searchfor ); char pattern = “ould”; /* pattern to search for */ /* find all lines matching pattern */ main ( ) char lineMAXLINE; int found = 0; while (getline(line, MAXLINE) 0) if (strindex(line, pattern) = 0) printf(“%s”, line); found+; return found; ,主函数,/*getline: get line into s, return length*/ int getline (char s , int lim) int c, i; i = 0; while ( -lim 0 ,子函数,4.2 返回非整数的函数,在使用返回值不是整数的函数时,正确地进行函数的定义和函数说明十分重要。 函数定义时一定要说明返回值类型,否则缺省为整数。 为了使调用函数知道被调用函数的类型,函数调用前一定要进行原形说明,否则,如果函数定义的类型与调用类型出现不一致: 如果调用和被调用函数在同一个文件中定义,编译器会提出警告,这是实行原型说明的好处。 如果不在一个文件中定义,编译器不会发现问题,即: 在调用前没有原型说明的函数,被理解为返回整数,而对它的变元毫无假定。 函数原型说明中没有给出变量说明的,编译器对变元不做任何检查。,子函数把字符串转换成浮点数,#include /* atof: convert string s to double*/ double atof (char s ) double val, power; int i, sign; for (i = 0; isspace(si); i+) /*skip leading spaces*/ ; sign = (si = -) ? -1 : 1; if (si = + | si = -) i+; for ( val = 0.0; isdigit(si); i+) val = 10 * val + (si - 0); if (si = .) i+; for (power = 1.0; isdigit(si); i+) val = 10 * val + (si - 0); power *= 10.0; return sign * val / power; ,主函数读入数字并把它们累加起来,#include #define MAXLINE 100 /* a rudimentary calculator */ main ( ) double sum, atof(char ); char lineMAXLINE; int getline(char line , int max); sum = 0; while (getline(line, MAXLINE) 0) printf(“t%gn”, sum += atof(line); return 0; ,4.3 外部变量-全局变量,全局变量是在所有函数外部定义的变量,它有如下的特性: 全程可见性:对程序中全局变量,通过适当说明,所有函授都可以通过它的唯一的“名字”对它存取,即使这些函数是在不同文件并且是分别编译的。 可见性可控:当然,也可以通过适当的说明,使某些全局变量只在一个文件中有效。 全程生命周期:全局变量从程序执行时就存在,并且一直存在直到程序结束。 可增加效率:可以用来保留中间结果,如果许多函数使用大量的共同数据,适度的使用外部变量,可以避免大量数据的传递,对于简化程序是有益的。 函数是全局的:C程序是由一系列外部变量和函数组成的,C中所有的函数都是全局的,不能在函数中定义函数。,局部变量或内部变量(2),全局变量又叫外部变量,它与内部变量相对应,函数的参数变量和函数内部定义的变量叫内部变量。 内部变量的可见性是局部的:内部变量又称局部变量,只在函数内可见,当局部变量与某全局变量同名时,全局变量受到屏蔽。其好处是,不同的函数的局部变量可以同名,他们之间不会产生混淆。 局部变量的生命周期短:从函数被调用的时刻起存在,到函数调用结束时消亡。 函数的局部动态变量在两次调用之间没有关系,更不会相互影响。 使用局部变量,可以保证程序的模块化 但在特定的情况下使用全局变量会简化参数的传递。权衡,例子:说明外部变量的正确使用,设计一个可以做加减乘除的计算器,输入采用反序波兰表达式: 1 2 - 4 5 + * 表示:(1 - 2)*(4 + 5) 处理的算法如下: while(下一个字符是操作数或操作符并且不是文件结束) if (是数字) 压入栈中 else if (是操作符) 弹出操作数 实行操作 压入栈中 else if (是新行) 弹出操作数并打印结果 else 出错 这里的主要问题是决策:公共存取的变量放在何处?-外部还是局部?,主程序,#include #include #define MAXOP 100 #define NUMBER 0 int getop(char ); /*原型说明*/ void push(double); double pop(void): /*reverse polish calculator*/ main ( ) int type; double op2; char sMAXOP; while (type = getop(s) != EOF) switch(type) case NUMBER: push(atof(s); break; case +: push(pop( ) + pop( ); break; case *: push(pop( ) * pop( ); break; case -: op2 = pop(); push(pop( ) - op2); break; case /: op2 = pop(); if (op2 != 0.0) push(pop( ) / op2); else printf(“error: zero divisorn”); break; case n:printf(“t%.8gn”, pop( ); break; default: printf(“error: unknown command %sn”, s); break; return 0; ,子程序1,/*解决堆栈和出栈的数据存储和操作* /#define MAXVAL 100 int sp = 0; double valMAXVAL; /*value stack*/ /*push: push f onto value stack*/ void push (double f) if (sp 0) return val-sp; else printf(“error: stack emptyn”): return 0.0; ,子程序2,#include int getch(void); void ungetch(int); /*getch: get next operator or numeric operand 解决数据和操作符的输入*/ int getop (char s ) int i, c; while(s0 = c = getch( ) = | c = t) ; s1 = 0; if ( !isdigit(c) ,子程序3,/*缓冲式的字符输入*/ #define BUFSIZE 100 char bufBUFSIZE; /* buffer for ungetch*/ int bufp = 0; /*next buffer position*/ /* get a character (possibly pushed back) */ int getch(void) return (bufp 0) ? buf-bufp : getchar( ); /* ungetch: push character back on input */ void ungetch(int c) if (bufp = BUFSIZE) printf(“ungetch: too many charactersn”); else bufbufp+ = c; ,4.4 变量辖域,某变量的辖域是指可以通过该变量的名字对之存取的程序部分。 函数的形式参数和在函数前部定义或说明的局部变量的定义域是整个函数。不同函数中同名的变量间没有任何关系。 在程序开头定义的全局变量的辖域是所在文件的这个变量定义以后的其余部分。 但是一个程序可以分放在几个文件中,预先编译好的程序也可以从程序库中导入,连接成更大的程序。由于存在一个文件使用另一个文件中定义的全程变量,此时存在如下问题: 如何写变量的说明使得能正确编译? 如何安排变量说明,使得程序装入内存时能正确地连接? 如何保证是外部变量只有一份拷贝? 如何对全局变量初始化? 下面回答这个问题:,变量辖域(续),外部变量的辖域是从定义或说明它们的地方起直到文件的结束: main( ) . int sp = 0; double valMAXVAL; void push(double f) . double pop(viod) . 如果要在定义函数时需要访问尚未定义的外部变量或需要访问另一文件中定义的外部变量,则在使用它的文件(或函数)的开头必须加外部变量说明。说明以后它在文件(或函数)的其余部分是可见的了。 要注意外部变量的定义和说明之间的区别,说明只指出变量的类型,定义还涉及内存的分配。在整个程序中一个外部变量只能定义一次,其它文件用extern说明加以关联。 外部变量只能在定义的地方初始化一次,不能在extern说明时进行初始化。,正常的情况是:main无法存取它后面定 义的变量和函数。如果main要存取sp,val, push,和pop,则必须在这里或main 的开头加: extern int sp; extern double val ; void push(double); double pop(void):,4.5 头文件,当程序分成好几个文件时,为了共享外部变量和函数,一般的做法是把说明集中写在一个头文件中,然后在必要的文件中加以包含:,/*calc.h*/ #define NUMBER 0 void push(double); double pop(void); int getop(char ); int getch(void); void ungetch(int);,/*getop.c*/ #include #include #include “calc.h” getop( ) ,/*getch.c*/ #include #define BUFSIZE 100 char bufBUFSIZE; int bufp = 0; int getch(void) . void ungetch(int c) . ,/*main.c*/ #include #include #include “calc.h” #define MAXOP 100 main( ) ,/*stack.c*/ #include #include “calc.h” #define MAXVAL 100 int sp = 0; double valMAXVAL; void push(double f) double pop( ) . ,针对性问题:只有在 特别大的程序中才考 虑使用多个头文件,4.6 静态变量,为了使一个外部变量的辖域仅限于一个文件,不让其它文件存取这个变量,或者让其它文件可以使用同样的外部变量名而不与这个变量相混淆,可以在该外部变量之前加static前缀: 函数也可以说明成为static,说明成static的函数只在本文件可见。 局部变量也可以说明成static, 静态局部变量仍然是局部的,但它有永久的、私有的内存空间,因此函数调用的结果可以保留下来,直到此函数以后的调用中将之改变。这一特性十分重要!,/*getch.c*/ #include #define BUFSIZE 100 static char bufBUFSIZE; static int bufp = 0; int getch(void) . void ungetch(int c) . ,/*stack.c*/ #include #include “calc.h” #define MAXVAL 100 static int sp = 0; static double valMAXVAL; void push(double f) double pop( ) . ,4.7 寄存器变量,对于常用的变量(特别是循环变量)可以说明成寄存器变量,要求编译器将其安排在机器的寄存器中,其格式是在说明之间加register前缀: register int x; register char c; 寄存器变量存取的速度快,但只能把局部变量和函数的形式参数说明成寄存器变量,而且也只有一定类型(整型)的变量能被指定成寄存器变量: fun (register unsigned n, register long n) register int i; 根据机器的不同,可同时指定为寄存器变量的数目不同,编译器可对过多的寄存器变量说明不予理会。一般说来,只把使用频繁的循环变量指定为寄存器变量。 寄存器变量没有地址,不管它实际存放在寄存器上与否。,4.8 块结构,C语言不是严格的块结构语言,不能在函数内定义函数,但可以在函数内甚至程序块内定义局部变量: if (n 0) int i; for (i = 0; i n; i+) /* 这变量只在程序执行这个块时建立,块结束时消亡*/ 局部变量对块外的同名变量和函数会起屏蔽作用,如: int x; int y; fun (double x) double y; /* 在函数内,这两个变量与外部的变量无关,这也是一个十分重要的特点,编程时最好避免这种情况 */,这个 i 是局部于这个程序块,4.9 变量初始化,在没有明确指定初始化公式情况下:全程变量和静态变量初始化为0。自动变量(局部和参数)和寄存器变量的初值是不确定的。 数值性的变量可以初始化: char squote = ; long day = 1000L*60L*60L*24L; 全局变量和静态变量的初始化公式只能是常数表达式,并且只初始化一次(程序执行前);局部的非静态变量初始化公式可以是任意的,而且在每一次进入函数时进行一次初始化。 int bisearch (int x, int v , int n) int low = 0, high = n - 1, mid; 数组的初始化: int days = 31,28,31,30,31,30,31,31,30,31,30,31; 对于任何类型的变量,在指明元素个数时,初始化不足的元素为0;初始化过多时出错;没有办法表示元素值重复;无法省略前导元素 字符数组的初始化可以使用如下特殊的形式: char s = “hello”; /*它是下式的简写*/ char s = h,e,l,l,o,0;,4.10 递归,C语言允许递归调用,即一个函数直接或间接地调用它自身。有许多需要递归调用的场合,例一,打印一个整数,由于最先得到的是最低位,但最先要打印的是最高位,递归可以解决此类问题: #include /* printd:print n in decimal */ void printd(int n) if (n 0) putchar(-); n = -n; if (n / 10) printd(n / 10); putchar(n % 10 + 0); 另一个例子是快速排序,给定一个数组,选择一个元素,把其它元素根据比该元素大小分成两个集合,然后递归调用快速排序算法,直到集合的元素少于两个,排序完成:,递归(续),/*qsort: sort vleft . vright into increasing order */ void qsort(int v , int left, int right) int i, last; void swap(int v , int i, int j); if (left = right) return; swap(v, left, (left + right) / 2); /* move partition element to elem */ last = left; for (i = left+1; i = right; i+) if (vi vleft) swap(v, +last, i); swap(v, left, last); /* restore partition elem */ qsort(v, left, last - 1); qsort(v, last + 1, right); /* swap: interchange vi, and vj */ void swap(int v , int i, int j) int temp; temp = vi; vi = vj; vj = temp; ,许多问题既可以用递归方法,也可以用非递归方法:在许多情况下,用递归写的程序会更紧凑、更容易理解,特别对于处理递归设计的数据特别方便,但是递归在内存和时间上都不会节省。,4.11 C预处理器,C在文件编译之前对被编译的文件提供预了许多预处理手段,其中包括:文件包含;宏定义;条件编译。 1.文件包含: 一般格式: #include /* 在缺省的文件目录寻找filename */ #include “filename” /* 在原程序所在的目录寻找,若找不到 在缺省的文件目录寻找filename */ #include将由filename的文件内容所取代。,应用文件包含的目的是把个文件共同需要的宏定义, 外部变量说明,库函数的原型说明等(stdio.h)插入到文 件的开头。这样可更好地保证每一个文件需要的常数 定义和子函数的定义都是一样的。,C预处理器(续),#define(宏代换) 一般格式: #define #define forever for( ; ; ); /*在编译前将文本中的前者(不 包括在字符串中的)用后者替换*/ 可带变量 #define max(A, B) (A) (B) ? (A) : (B) x = max(p+q,r+s) 替换成x = (p+q) (r+s) ? (p+q) : (r+s) 尽管宏定义是十分有用的,但要注意如下的调用或定义的问题-特别要注意重复执行问题和运算的优先级问题: max(i+, j+); #define square(x) x*x #把实参变成字符串 #define dpri

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论