第四章 函数 一. 一. 带子程序的 c 程序 带子程序的 c 程序 函数 函数...

53
第第第 第第 一. 带带带带带 C 带带 带带 带带带带带带

Post on 21-Dec-2015

509 views

Category:

Documents


16 download

TRANSCRIPT

第四章 函数

一. 带子程序的C程序

二. 函数

三. 程序设计实例

带子程序的 C 程序

编程计算

其中( 0.3, ) / 2

( )2

f u x u up u

x

2 5( ) ( 2)

( 5) -

p x xy x p x

p x x

( , )7

w vf v w

v

void main(){ float y,f,p0,p2,p5,x;

printf ( "please input x: ");

scanf("%f",&x);

f=((x+x)+(x*0.3))/(7*(x*0.3));

p0=(f+x/2)/(2*x);

f=((x+(x+2))+((x+2)*0.3))/(7*((x+2)*0.3));

p2=(f+(x+2)/2)/(2*x);

f=((x+(x+5))+((x+5)*0.3))/(7*((x+5)*0.3));

p5=(f+(x+5)/2)/(2*x);

y=((p0*p0+5*x)/(p5-sqrt(x)))*p2;

printf ("y(x)=%8.4f\n",y);

}

0p p x0.3

( 0.3, )7 0.3

x x xf x x x

x

2 0.3 2 22 0.3, 2 2

7 2 0.3

x x xf x x x

x

2 2p p x

5 0.3 5 55 0.3, 5 5

7 5 0.3

x x xf x x x

x

5 5p p x

float x,y ; /* 3 */

float f(float v,float w ){ /* 4 */

return (w+v)/(7*v); /* 5 */

} /* 6 */

float p(float u){ /* 7 */

return ( f(u*0.3,x+u)+(u/2))/(2*x) ); /* 8 */

} /* 9 */

void main(){ /* 10 */

printf ("please input x: "); /* 11 */

scanf("%f",&x); /* 12 */

y=(p(x)*p(x)+(5*x))/(p(x+5)-sqrt(x))*p(x+2); /* 13 */

printf ("y(x)=%8.4f\n",y) /* 14 */

}

程序执行的主要步骤– 从键盘读入 x 的值

printf ("please input x: ");

scanf("%f",&x);

– 依次计算以 x, x+5, x+2 作为实参的函数 p 的值y=(p(x)*p(x)+(5*x))/(p(x+5)-sqrt(x))*p(x+2)

– 输入出函数 y 的值printf ("y(x)=%8.4f\n",y)

计算 p(x+5) 过程 – 计算 x+5 得 6.0 ;– 将 6.0 送入 p 的形参 u 中,这时 u 的值为 6.0 ;– 进入函数 p ,执行 p 的操作部分 ( 第 8 行 ), 即返回语句“ ret

urn ... ;” ,先计算返回语句中表达式的值。 以 u*0.3 和 u+x 为实参调用函数 f

– 顺序计算 u*0.3 、 u+x 得 1.8 、 7.0 ;– 将 1.8 、 7.0 顺序送入 f 的形参 v 、 w 中, v=1.8 、 w=7.0 ;– 进入函数 f 执行 f 的操作部分 ( 第 5 行 ) ,执行返回语句– 计算出表达式的值为 0.698413

– 带着函数值 0.698413 返回调用处, p 中的返回语句内 ( 第 8 行 )

计算出返回语句中表达式的值为 1.84921 即 p 的值。

– 带着函数值 1.84921 返回调用处:主程序第 13 行的表达式语句内。

主程序

调用函数

返回

结束

函数

使用子程序技术分离了“做什么”与“怎么做”

– 程序逻辑结构清晰,易写,易读,易懂。

– 程序的设计,调试,维护变得容易。

函数 数学中函数

函数概念的组成

– 函数定义– 函数调用

使用函数注意事项– 先定义该函数(标准库函数除外)

——类似于数学中的函数定义。– 再在表达式中调用该函数

—— 数学中计算某函数的一个特定值。

yxyxf ),(

函数调用 (function-designator)

调用过程– 形参实参结合– 进入函数执行复合语句

一般形式– F (e, e, …, e)

– F ( )

例子– p (x+5)

– f (u*0.3,x+u)

函数定义 (function-declaration) 除标准库函数外 , 程序中使用函数必须先定义,然后

再用“函数调用”调用它。 标准函数是系统已经定义好的函数,不必定义即可直

接调用。 在程序 example401A中: float f(float v,float w ){ /* 4 */

return (w+v)/(7*v); /* 5 */

} /* 6 */

float p(float u){ /* 7 */

return ( f(u*0.3,x+u)+(u/2))/(2*x) ); /* 8 */

} /* 9 */

由上述语法可知,一个函数定义的形式是 :

类型说明符 标识符 ( 参数列表 ) 复合语句

float f (float u)

{

return (f(u*0.3,x+u)+(u/2))/(2*x);

}

函数类型

函数名字

形参列表复合语句

函数体函数定义说明符

函数定义说明符 上述形式的第一行称“函数定义说明符”,形式如

下 TT F ( 参数列表 )

具体指明以下点:函数的结果类型

——由“类型说明符” ( TT ) 标明;函数的名字

——由类型说明符后的“标识符” ( F ) 标明;函数的形式参数个数和每个形式参数的特性

—— 由“参数列表”标明。

函数类型(返回值的类型)– 不能是数组或函数– 没有返回值的函数类型是 void

函数返回– 返回方式:

return ;return 表达式;函数运行到复合语句末尾(最后那个闭

花括号“ }” )后。

int f (int x){return x*x;

}

int y=9;void f (int x){

if (x<0)return;

y=y+x;}

参数列表 (parameter-list)

– 组成: 每个参数声明具体说明形式:

类型说明符 标识符 参数列表形式

T id , T id , ... , T id

– C 允许使用无参函数,无参函数的参数列表为空,或使用“空类型”的类型说明符“ void”

TT F() TT F (void )

int f(int x, int y, float z)int f(int x, int y, float z)

int f(int x,y ; float z)int f(int x,y ; float z)

int f(int x,y , float z)int f(int x,y , float z)

根据参数出现的位置– 形式参数——函数声明 ( 局部于函数的变量 )

int f(int x,int y){

return x+y;

}

– 实际参数——函数调用void main(){

int a=2,b=3;

f(a+b, b);

f(2+b,3);

}

根据参数值的传递规则– 值参

当调用函数时,把实参的值传入形参变量– 变参当调用函数时,把实参的地址值传入形参变量

CC 语言中的参数都是语言中的参数都是值参值参

3

int f (int x, int y){ return x*y;}void main(){

int a=3, b=2,x=9;int c;c=f(a+b,a+x);printf(“%d\n”, c);printf(“%d\n”, x);

} 2

9

程序输出为 :

60

9

a

b

x

返回值

x

y

c

main

f

内存

5

12

60

60

复合语句( compound-statement )由声明和语句列表组成

– 声明部分具体的说明本函数内使用的其它量;

– 语句部分规定在本函数中要执行的算法动作。 函数定义的形式:

TT F ( T id , T id , ... , T id ){

...

}

参数结合规则– 静态上看,实参表中的实参与被调用函数中形

参表的形参,按位置从左向右依次一一对应– 对应位置上的形实参间要赋值兼容– 各个实参的计算次序是依赖于实现的。

参数结合动作– 计算实参表达式的值– 把实参的值按赋值转换规则,转换成形参的类

型。如果不能完成该转换,则称函数参数不一致,产生错误

– 把转换后的实参值送入形参

0x

y

v

z

u

main

f

内存

……

g 返回值

返回值

由左至右#include "stdio.h"

int x,y ;

int f( int z ){

x=x+1 ;

return (z*z);

}

int g( int u , int v ){

… …

}

void main(){

x=0 ;

g(x,f(2)) ; }

0

2

1

4

4

0x

y

v

z

u

main

f

内存

……

g 返回值

返回值

由右至左#include "stdio.h"

int x,y ;

int f( int z ){

x=x+1 ;

return (z*z);

}

int g( int u , int v ){

… …

}

void main(){

x=0 ;

g(x,f(2)) ; }

1

2

1

4

4

并行计算 ----- 不知道结果#include "stdio.h"

int x,y ;

int f( int z ){

x=x+1 ;

return (z*z);

}

int g( int u , int v ){

… …

}

void main(){

x=0 ;

g(x,f(2)) ; }

函数值 有返回类型的函数 int f (int x, int y){

if (x>=y) return x; else return y;}

无返回类型的函数 void g( int w){

if (w==1) return; else x=3;}

有返回类型函数的函数值– 使用 return e; 向调用函数的主程序传递函数值– return e ; 的执行过程是:

计算表达式 e 的值 把表达式值按赋值转换规则,转换成函数的结

果类型; 返回语句中表达式的类型与函数的结果类型必

须赋值兼容。 用类型转换后的值作为函数值,并带着它返回

到调用该函数处

无返回类型函数的函数值void g( int w){

if (w==1) return; else x=3;}

在函数调用处,所调函数无值可以带回。– 对于无类型函数,在函数调用处不需要函数值,

这种返回是正常的;– 对于有类型函数,在函数调用处极可能需要函数

值参加下一步运算,这将带来不可预料的结果

函数原型–为什么使用函数原型

任何标识符都必须声明,而且必须先声明后使用

在声明定义函数时必须保证函数声明位置在使用前出现–从程序行文顺序上控制 ——一般比较难实现–函数原型—— 方便灵活

一般形式TT F ( T , T , ... , T );

TT F ( T id , T id , ... , T id );

例子 float f ( int, float, int, char ) ;

float f ( int z, float u, int v, char w ) ;

函数原型的功能–满足了 C 标识符先定义后使用的要求– 并向编译系统提供所调用函数的信息

函数返回类型函数的参数个数函数参数特性等信息

– 程序设计风格最好把所有函数原型集中,放在主函数之前

float x,y ;

float f(float ,float );

float p(float );

void main(){

printf ("please input x: ");

scanf("%f",&x);

y=(p(x)*p(x)+(5*x))/(p(x+5)-sqrt(x))*p(x+2);

printf ("y(x)=%8.4f\n",y)

}

float f(float v,float w ){

return (w+v)/(7*v);

}

float p(float u){

return ( f(u*0.3,x+u)+(u/2))/(2*x) );

}

例 4-3 生成伪随机数

生成伪随机数公式

rk = ( multiplier * rk-1 + increment ) % modulus

如下给定三个常数值 , 产生 65536 个随机数multiplier = 25173

increment = 13849

modulus = 216 = 65536

#define multiplier 25173

#define increment 13849

#define modulus 65536

int seed=0 ;

int random ( void ) {

seed = ( multiplier * seed + increment ) % modulus ;

return seed ;

}

模拟掷骰子,函数修改成 :

int random ( void ) {

seed = ( multiplier * seed + increment ) % modulus ;

return seed % 6 +1 ;

}

产生 0 与 1 之间的实数型随机数序列,函数修改成 :

float random ( void ) {

seed = ( multiplier * seed + increment ) % modulus ;

return (float)seed / modulus;

}

例 4-4 验证 Pascal 定理 圆内接六边形三双对边延线的交点在一条直线上

A

BC

DE

F

B1

B2

B3

读入六个极角的值求六个顶点 A, B, C, D, E, F 在直角坐标系中的坐标

求三双对边交点 B1, B2, B3 坐标

验证 B1, B2, B3 是否在一条直线上

开始

结束

将 6 个点的极坐标转换成直角坐标

已知极角 theta,矢径 r,求一点的直角坐标 (px, py)

coordinate

px=r*cos(theta);

py=r*sin(theta);

return

trans_abcdef

转换 a → xa , ya

转换 b → xb , yb

转换 d → xd , yd

转换 f → xf , yf

转换 e → xe , ye

转换 c → xc , yc

return

求交点 B1,B2,B3 的坐标 已知四点 r, s, t, u求两条直线交点 B

three_inter

求 AB 、 DE交点 B1

求 BC 、 EF交点 B2

求 CD 、 FA交点 B3

return

intersection

求 r, s ; t, u直线方程 l1, l2

求 l1 、 l2交点 B

return

已知四点 r, s, t, u求两条直线 l1,l2 的方程

已知两点坐标 px, py, qx, qy, 求直线的斜率 a 和截距 b

eqution

return

求 r , s 直线方程 l1

求 t , u 直线方程 l2

straightline

b=(py*qx-qy*px)/(qx-px)

return

a=(py-qy)/(px-qx)

已知两条直线方程斜率分别为 ma, na; 截距mb, nb;求直线交点 (wx, wy)

验证 B1, B2, B3是否在一条直线上

inter

wx=(nb-mb)/(ma-na); wy=ma*px+mb;

return

test

return

求过 B1, B2 直线方程 L

判断 B3 是否在直线 L 上

/*PROGRAM Pascal theorem*/

#include "math.h"

#include "stdio.h"

#define PI 3.1415927

#define eps 1e-5

float radius; /* 圆的半径 */

float theta1, theta2, theta3, theta4, theta5, theta6;

/ * 六个极角的度数 */

float xa, ya, xb, yb, xc, yc, xd, yd, xe, ye, xf, yf;

/* 六个顶点的直角坐标 */

float b1_x, b1_y, b2_x, b2_y, b3_x, b3_y;

/* 三个交点的直角坐标 */

float b12_a, b12_b;

/*B1 和 B2 构成直线的斜率和截距 */

/* 主程序之前这段为“函数原型”以及各个函数返回结果所用变量 */

void trans_abcdef();

float px, py; /* 用来保存 coordinate() 转换的直角坐标 */

void coordinate (float, float);

void three_inter();

void intersection (float, float, float, float, float, float, float, float);

float l1_a, l1_b, l2_a, l2_b; /* 两条直线的斜率和截距 */

void equation (float, float, float, float, float, float, float, float);

float a, b; /* 直线方程的斜率和截距 */

void straightline(float, float, float, float);

float wx, wy; /* 直线交点的直角坐标 */

void inter (float, float, float, float);

int test (float, float, float, float, float, float);

/* 主函数 */void main(){ /* 读入圆形的半径 */ printf("please input the radius of the circle:"); scanf("%f", &radius); /* 读入六个角 */ printf("please input six angle:"); scanf("%f %f %f %f %f %f", &theta1, &theta3, &theta3, &theta4, &theta5, &theta6); trans_abcdef(); /* 计算六个定点坐标 */ three_inter(); /* 求三个交点 */ if( test(b1_x, b1_y, b2_x, b2_y, b3_x, b3_y) ) /*验证 */ printf("ok"); else{ printf("There is an error when:\n"); printf("theta1=%d theta2=%d\n", theta1, theta2); printf("theta3=%d theta4=%d\n", theta3, theta4); printf("theta5=%d theta6=%d\n", theta5, theta6); }}

/* 计算六个顶点坐标 */

void trans_abcdef(){

coordinate(radius, theta1);

xa=px; ya=py;

coordinate(radius, theta2);

xb=px; yb=py;

coordinate(radius, theta3);

xc=px; yc=py;

coordinate(radius, theta4);

xd=px; yd=py;

coordinate(radius, theta5);

xe=px; ye=py;

coordinate(radius, theta6);

xf=px; yf=py;

}

/* 计算一个顶点坐标 */

void coordinate(float r, float theta) {

/* 先把“角度”转换成“弧度”,再转换成直角坐标 */

px=r*cos(PI*theta/180);

py=r*sin(PI*theta/180);

}

/*求三个交点 */

void three_inter(){

intersection(xa, ya, xb, yb, xd, yd, xe, ye);

b1_x=wx; b1_y=wy;

intersection(xb, yb, xc, yc, xe, ye, xf, yf);

b2_x=wx; b2_y=wy;

intersection(xc, yc, xd, yd, xf, yf, xa, ya);

b3_x=wx; b3_y=wy;

}

/* 已知四点,求两条直线交点 */

void intersection(float rx, float ry, float sx, float sy,

float tx, float ty, float ux, float uy){

equation (rx, ry, sx, sy, tx, ty, ux, uy);

inter (l1_a, l1_b, l2_a, l2_b);

}

/* 已知四点,求两条直线方程 */

void equation ( float rx, float ry, float sx, float sy,

float tx, float ty, float ux, float uy){

straightline(rx, ry, sx, sy);

l1_a=a; l1_b=b;

straightline(tx, ty, ux, uy);

l2_a=a; l2_b=b;

}

/* 计算由两点确定直线方程的斜率( a )和截距( b ) */

void straightline(float ex, float ey, float fx, float fy){

a=(fy-ey)/(fx-ex); /* 斜率 */

b=ey-a*ex; /* 截距 */

}

/* 已知两个直线方程的斜率和截距,求它们交点 */

void inter ( float ma, float mb, float na, float nb){

wx=(nb-mb)/(ma-na);

wy=ma*wx+mb;

}

/* 检验 */

bool test ( float b1_x, float b1_y,

float b2_x, float b2_y,

float b3_x, float b3_y) {

straightline(b1_x, b1_y, b2_x, b2_y);

if(fabs(b3_y-(a*b3_x+b))<eps)

return true;

else

return false;

}

本章小结

函数的定义

函数的调用函数的程序设计思想

作业作业①① 4.14.1

②② 4.84.8

③③ 4.124.12