1、高级语言程序设计(一) (C Programming),第二讲:C程序设计基础(二),C程序设计基础(二),本章目标,掌握函数的定义及调用方式 掌握函数参数传递方式 掌握一维数组的定义和使用 掌握简单的文件输入/输出,C程序设计基础(二),模块化程序设计,将复杂问题分解为简单问题的程序设计方法称为结构化程序设计,其特点为: 自顶向下(top-down design); 逐步细化(stepwise refinement); 模块化(modular programming); 模块化的好处: 功能分解的需要; 代码重用;,C程序设计基础(二),# include main( ) int a, b,
2、 sum;scanf( “%d%d”, ,标准输入/输出库函数,C程序设计基础(二),标准(库)函数,标准I/O库函数 #include ( scanf,printf, getchar, putchar) 标准数学函数 #include (sin, cos, sqrt) ,C程序设计基础(二),问题2.1,问题: 已知一组三角形的三边(如(2.0, 2.0, 2.0)、(3.0,4.0,5.0)、(2.5, 3.1, 3.8)),计算其面积。计算三角形面积的公式为:,#include #include main () double s;s = (2.0 + 2.0 + 2.0) / 2;pri
3、ntf(“(2.0,2.0,2.0) area = %fn”, sqrt(s*(s-2.0)*(s-2.0)*(s-2.0);s = (3.0 + 4.0 + 5.0) / 2;printf(“(3.0,4.0,5.0) area = %fn”, sqrt(s*(s-3.0)*(s-4.0)*(s-5.0);s = (2.5 + 3.1 + 3.8) / 2;printf(“(2.5,3.1,3.8) area = %fn”, sqrt(s*(s-2.5)*(s-3.1)*(s-3.8); ,对于这样的有规律的重复计算,可以用更好的方法:函数 来解决,double sqrt(double x)
4、 为标准数学库中函数,使用前应加上 #include ,C程序设计基础(二),定义求三角形面积函数,tri_area( ),double a,double b,double c,double,函数名,需要传递给函数的数据(参数):数据类型、数据名称,函数计算结果(返回)类型double,函数是如何对数据进行计算 (函数体),double a, double b, double c,tri_area,计算 语句,C程序设计基础(二),tri_area函数定义,double tri_area(double a, double b, double c) double s,area;s = (a +
5、b + c) / 2.0; area = sqrt(s*(s-a)*(s-b)*(s-c); return (area); ,函数定义头部,其中 double 为函数类型 tri_area为函数名 a,b,c为函数参数,函数体,局部变量,函数返回(return)语句,返回计算结果,C程序设计基础(二),函数定义与调用,在ANSI C标准中,函数定义形式为:类型 函数名(参数说明) 局部变量定义或说明语句 ,可以没有,多个参数以逗号分隔,C程序设计基础(二),函数定义与调用(续),函数名一般是标识符,一个程序只有一个main函数,其它函数名可随意取,当然最好是有助于记忆的名字,如getchar函
6、数。在ANSI C标准中,函数(返回值)类型不允许省略,即使是返回整型值(int),当函数无返回值时,应其类型说明为void类型。局部变量定义或说明可有可无。注意:在C语言中,函数定义不允许嵌套,即在一个函数体内不能包含有其它函数的定义。,若无返回值,函数用: return; 或者函数中没有return语句,C程序设计基础(二),问题2.1:代码实现,double tri_area(double a, double b, double c) double s,area;s = (a + b + c) / 2.0; area = sqrt(s*(s-a)*(s-b)*(s-c); return
7、(area); ,#include #include int main( ) printf(“(2.0,2.0,2.0) area = %fn”, tri_area(2.0,2.0,2.0);printf(“(3.0,4.0,5.0) area = %fn”, tri_area(3.0,4.0,5.0);printf(“(2.5,3.1,3.8) area = %fn”, tri_area(2.5,3.1,3.8);return 0; ,double tri_area(double a, double b, double c);,函数调用,函数原型,C程序设计基础(二),函数定义与调用(续),
8、函数调用形式:函数名(实参表)其中实参个数、类型、排列次序应和形参定义时一致。(老版本的C编译器往往不做这方面的检查)函数通过return语句将值返回给调用函数。它有两种使用形式: 1)return expr; (函数有返回值) 2)return; (函数无返回值,对应函数类型为void) 注意:使用return语句只能返回一个值。,C程序设计基础(二),函数(返回值)类型说明,即函数定义时,函数返回值类型的说明,在ANSI C标准中,函数类型必须显式说明,当函数无返回值时(即“过程”),则函数类型可说明成void。 在C语言中,函数返回值的类型可以是基本类型或指向其它类型的指针(也可返回结构
9、或联合类型)。,C程序设计基础(二),函数原型说明(prototype),在ANSI C标准中,所有函数必须要有原型说明,用以说明函数的返回值类型、函数参数类型、个数及次序。函数原型说明有两种形式: 直接使用函数的头部。如,double tri_area(double a, double b, double c); 在原型说明中仅给出类型、个数及次序,无形参变量名。如, double tri_area(double , double, double ); 函数原型类似于后面将介绍的外部变量说明,在调用任何函数之前必须确保其已有函数原型说明。注意:函数原型说明的类型、参数类型、个数及次序必须与函
10、数定义时一致,否则会产生错误。,C程序设计基础(二),函数参数,调用函数时,实参的类型、排列次序和个数应与函数定义时形参相对应(在ANSI C标准中,若不一致,将出现编译错误)。 C函数的参数传递全部采用传值。传值调用实际上重新拷贝了一个副本给形参,因此,我们可以把函数形参看作是局部变量。传值的好处是传值调用不会改变调用函数实参变量的内容,因此,可避免不必要的副作用。,C程序设计基础(二),函数参数(续),如何理解“传值”调用?让我们来看一个试图交换两个数据值的例子。 void swap ( int x, int y) int temp;temp = x; x = y; y = temp; m
11、ain( ) int a = 2, b = 3; printf(“a=%d, b=%dn”, a, b); swap(a, b); printf(“a=%d, b=%dn”, a, b); ,请问a和b是否交换?,不能!,如何通过函数调用改变参数的值将在以后介绍。,C程序设计基础(二),函数参数(续),由于函数调用时,参数传递是传值,因此,在函数定义中的形参可当局部变量使用。下面求幂(xn)函数power的实现是等价的:,double power(double x, int n) int i; double p=1; for(i=1; i=n; +i)p = p*x; return (p);
12、,double power(double x, int n) double p;for(p = 1; n 0; -n)p *= x;return (p); ,C程序设计基础(二),问题2.2:求素数,问题2.2:读入一个整数,求该整数范围内的所有素数。 解题步骤: 读入一个整数到n; for(m=2; m=n; i+) if(m是素数)输出m;,可定义一个函数 int isprime(int n) 来判断一个整数是否为素数,若是返回1,否则返回0。,C程序设计基础(二),问题2.2:代码实现,int isprime(int n) int i; if(n=1) return 0; for( i=
13、2; i*i = n; i+) if(n%i = 0) /*存在因子,不是素数*/ return 0; return 1; ,主函数 #include int isprime(int n); int main() int n,m; scanf(“%d”, ,C程序设计基础(二),局部变量,局部变量(local variable),又称自动变量:在函数(或块结构)中定义的变量。 只在定义它的函数或块结构内有效(即其使用范围,称为作用域scope)。 编译程序不对局部(自动)变量给予隐含的初值,故其初值不确定。因此,每次使用前,必须明确地置初值。 局部(自动)变量随函数的调用而存在,函数返回后将消
14、失,由一次调用到下一次调用之间不保持值,每次调用函数时都重新初始化。 形参是自动变量,使用范围仅限于相应函数内。,C程序设计基础(二),如何划分函数,程序中可能有重复出现的相同或相似的计算片段,可以考虑从中抽取出共同的东西,定义为函数。这样可以缩短程序代码,提高程序的可读性和易修改性。 程序中具有逻辑独立的片段定义为函数。这样做主要用于分解程序的复杂性。,C程序设计基础(二),问题2.3,问题: “从标准输入中输入10个整数,然后反序输出”。 输入样例: 2332 455 5 67 78 9 10 467 3 23 输出样例: 23 3 467 10 9 78 67 5 455 2332,C程
15、序设计基础(二),问题2.3:问题分析,首先遇到的问题是如何保存输入的数据? 以目前所学的知识,我们可以设置10个变量来存储输入如: int data1, data2, , data10; 这样做的缺点: 程序处理数据非常烦琐,如我们必须依次读入每个数据(不能用循环); 程序不具扩展性,如果我们要处理100个、1000个甚至更多的数据,怎么办? 如何 存储类型相同并且紧密相关的一组数据?,使用数组,C程序设计基础(二),数组的定义与初始化,数组是变量的有序集合,数组的所有成员(数组元素)都具有相同的类型。数组定义一般采用如下格式: 类型 数组名长度; 长度为常量表达式,C程序设计基础(二),数
16、组的定义与初始化(续),例如: int a50; a0 a49 float mc-a, p2*sizeof(double);,a0,a1,数组元素的下标是从0开始,即数组中第一个元素的下标为0,C程序设计基础(二),数组的定义与初始化(续),int length = 10; double slength;,长度必须是常量表达式,注意:C语言不支持动态数组,即数组的长度必须在编译时确定下来,而不是在运行中根据需要临时决定。但C语言提供了动态分配存贮函数,利用它可实现动态申请空间。,C程序设计基础(二),数组的定义与初始化(续),数组初始化:可以在定义时初始化一个数组。下面是一些数组初始化实例:
17、double sales5 = 12.25, 32.50, 16.90, 23, 45.68; double sales = 12.25, 32.50, 16.90, 23, 45.68; int list5 = 6, 5, 12 ; /相当于: int list5 = 6,5,12,0,0; int list5 = 0; char string10 = “hello”; char string10 = h, e, l, l, o, 0; char string = “hello”; 注意:用字符串常量初始化一个字符数组时,其长度应至少为字符个数多1。,C程序设计基础(二),数组元素的访问,初
18、始化数组 int array10, i; for(i=0; i10; i+)arrayi = 0; 读入数组元素 int array10, i; for(i=0; i10; i+)scanf(“%d”, ,注意:不要将循环条件写为i=10。这是初学者常犯的错误。如果一个数组的长度为N,则遍历数组的循环常写为: for(i=0; iN; i+) 或 for(i=0; i=N-1; i+),编译器不提供数组下标越界检查,下标越界时可以通过编译,但运行时会出错,C程序设计基础(二),数组元素的访问(续),数组中最大元素 maxIndex = 0; for(i=0; iN; i+)if(arrayma
19、xIndex arrayi)maxIndex = i; maxElement = arraymaxIndex;,C程序设计基础(二),数组处理的限制,在C中不允许对数组进行整体操作。下面用法是错误的: int x10, y10; scanf(“%d”, x); y = x; if( x = y ),正确做法是: for(i=0; i10; i+)yi = xi;,复制数组,比较数组,正确做法是: for(i=0; i10; i+)if(xi != yi),C程序设计基础(二),问题2.3:解决步骤,1、定义保存10个整数的数组data10和下标变量index; 2、从标准输入中依次读入10个整
20、数到数组data中; 3、从后往前输出数组data中每个元素;,C程序设计基础(二),问题2.3:代码实现:,# include int main( ) int data10, index;for(index =0; index =0; index- -)printf(“%d “, dataindex );return 0; ,依次读入10个整数到数组中。,从后往前输出数组中每个元素。,C程序设计基础(二),问题2.4,问题:统计输入中每个数字字符的出现次数 方法分析: 方法一:显然可以使用10个int型变量来分别存储每个数字字符的出现次数。然后在程序中,使用switch或if_else if
21、来分别统计每个数字字符的出现次数。(该方法太笨拙),C程序设计基础(二),问题2.4:方法分析(续),方法二 可以使用数组来存储每个数字字符的出现次数。如: int digit10; 如何将输入的数字字符与相应数组下标对应? 表达式:c 0 可将数字字符c转换为相应整数。因此,下面语句可统计每个数字字符的出现次数:digitc-0+;,C程序设计基础(二),问题2.4:代码实现,#include int main() int i, c, digit10 = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;while(c = getchar() ) != EOF)if(c = 0 ,C
22、程序设计基础(二),数组作为函数参数,数组可以作为参数传递给函数。实际上传递的是数组的首地址(即数组第一个元素的地址,将在指针部分说明),我们可以这样理解数组作为参数传递:形参数组与实参数组是一对共享同一数据区的数组,即它们是同一个数组,而不是对实参数组的拷贝。 数组作为参数时,函数的定义形式: void fun( int array , int size) 数组作为参数时,函数的调用形式: main() int a10;.fun(a, 10);. ,注意:定义数组形参时,数组长度可省略。对于非字符数组,还应在形参中指定数组元素个数。,注意:函数调用时,用数组名作实参。,C程序设计基础(二),
23、数组作为函数参数(续),/ 例2.3 统计字符个数 #include int main() int i, c, digit10 = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;while(c = getchar() ) != EOF)if(c = 0 ,如何将打印整数数组封装成一个函数?,void print(int a10) int i;for(i=0; i10; i+)printf(“ %dn”, ai); ,注意:数组长度不要写一维数组形参内,它无法传递给函数。,void print( int a , int length) int i;for(i=0; ilength;
24、i+)printf(“ %dn”, ai); ,print(digit, 10);,数组作为实参, 千万不要写成digit10,C程序设计基础(二),void count( int a ) int c;while(c = getchar() ) != EOF)if(c = 0 ,数组作为函数参数(续),数组中的值能 传递回来吗?,C程序设计基础(二),字符数组,数组元素的类型是char。 char mes = “C Language”; char line100 = “Programming”;字符数组有如下特点: 数组元素跟一般变量一样可赋值、比较、计算等。 数组下标也是从0 N-1(N为数
25、组长度)。 字符数组长度可以显式给出,也可以隐式得到。 由双引号括起来的的字符串常量具有静态字符串数组类型。 对数组初始化时,编译程序以0作为结束这个数组。因此,数组长度要比字符串长度多1。,C程序设计基础(二),问题2.5:将数字字符串转换成整数,n = 0; While si为数字字符n = 10*n + si 0;,“123”,1-0= 1,1*10 + 2-0 = 12,12*10 + 3-0 = 123,方法分析,C程序设计基础(二),问题2.5:代码实现,int atoi(char s ) int i, n, sign; for(i=0; si = = | si = = n | s
26、i = = t; i+) ; /* skip white space */ sign = 1; if(si = = + | si = = -) sign = (si+ = = +)?1:-1; for(n=0; si = 0 ,字符数组。,#include int atoi(char s ); main() char s20;scanf(“%s”, s);printf(“%dn”, atoi(s); ,空语句,条件运算符:? : 先计算表达式1,若其值为非零,则整个表达式结果为表达式2的值,否则就为表达式3的值。,C程序设计基础(二),问题2.5:将字符串颠倒,“”,方法分析,交换,C程序设计
27、基础(二),问题2.5:代码实现,void reverse(char s ) int c, i, j; for(i=0, j=strlen(s)-1; i j ; i+,j-) c = si; si = sj; sj = c; ,int strlen(char s ) int i=0; while(si != 0) +i; return (i); ,#include void reverse(char s ); Int strlen(char s ); main() char s20;scanf(“%s”, s);reverse(s);printf(“%sn”,s); ,字符数组作为函数参数传
28、递时,不需要同时传递数组长度。因为字符数组中字符串是以0结束的。,C程序设计基础(二),简单文件操作,到目前为止,所有读写操作均为标准输入/输出(即针对键盘及屏幕)。 实际应用多数都是针对文件输入/输出。,C程序设计基础(二),问题2.6:文件拷贝,问题: 将一个给定文件“input.txt”中内容拷贝到文件“output.txt”中。将标准输入拷贝到标准输出非常简单: #include int main() int c;while(c=getchar() != EOF)putchar(c);return 0; ,如何对文件读写?,C程序设计基础(二),文件输入/输出,文件输入/输出过程,打开
29、文件,首先在程序文件的头部应有如下语句: #include ,FILE *in, *out;in = fopen(“input.txt”, “r”); /为输入打开一个给定文件“input.txt”;打开方式”r”为打开一个只读文件。 out = fopen(“output.txt”,”w”); /为输出打开一个给定文件“output.txt” ;打开方式”w”为打开一个只写文件。,c =fgetc(in); /从文件(input.txt)中读入一个字符 fputc(c,out); /输出一个字符到文件(output.txt)中 fgets(s, n, in); /从 文件in上读入一行(最多
30、读入n-1个字符),放入s 字符数组中。返回s或NULL。fputs( s, out); /把字符串s(不一定含n)写入文件out中。返回非负数或EOF fscanf(in, “%d”, /输出一个整数到文件out中,fclose(in); / 关闭文件input.txt fclose(out); /关闭文件output.txt,C程序设计基础(二),问题2.6:代码实现,#include int main() int c;while(c= getchar() ) != EOF)putchar(c);return 0; ,FILE *in, *out;,in = fopen(“input.tx
31、t”, “r”); out = fopen(“output.txt”,”w”);,fgetc(in),fputc(c, out);,fclose(in); fclose(out);,为读写文件定义文件指针,打开文件 文件input.txt和output.txt位于与该执行程序.exe文件同一目录下(在VC中则与工程在同一目录下),从文件input.txt中依次读一个字符,向文件output.txt中依次写一个字符,关闭两个打开的文件,C程序设计基础(二),问题2.6:测试,首先在该程序工程文件(即.dsp文件)目录下准备一个包含一定内容的input.txt文件。 程序正确运行后,将在同一目录下生成一个output.txt文件。 检查文件input.txt和output.txt内容是否完全一样。,C程序设计基础(二),如何调试函数? 如何查看字符数据? 如何查看数组中的数据?(问题2.5颠倒字符串程序为例),程序设计实践:调试程序 ( 二 ),