第26题  按给定公式计算常数减去数列前N项、通项为二项积倒数

在本题程序MODI1.C中,函数fun通过形参m接收一个整型值,按以下公式计算常数减去数列前N项:

MODI26-00

例如:若m的值为5,则y的值为0.166667。

请改正程序中的错误,使之得出正确结果。                                                                注意:不许改动 main函数,不许增行或删行,也不许更改程序结构。

MODI1.C:

MODI26-01

参考答案:

MODI26-02运行结果:

MODI26-03

程序解析:

#include <stdio.h>

文件包含预处理指令#include <stdio.h>支持标准库函数printf、scanf的正常调用。程序要调用某个库函数,就必须包含与之对应的头文件。预处理指令通常位于源文件开头处,单独占一行,行尾无标点。

程序从函数main开始执行。声明:

int m;

定义整型变量m,分配存储,不作初始化,用于读取存储数列项数参数m,并充当调用函数fun的实参。凡未经初始化的变量,其初值无意义,仅反映新分配内存的当前随机性物理状态。至此,当前存储状态如下图所示,变量的无意义垃圾值以空白表示。

MODI26-04

在函数main中,首先执行下列语句:

printf(″请输入m的值:″);
scanf(″%d″, &m);

printf语句在标准输出设备(通常是显示器)给出提示信息。scanf语句从标准输入设备(通常是键盘)读取数列项数参数m的值于变量m,这里scanf的实参必须是指向变量m的整型指针&m,&是取地址运算符。至此,当前存储状态如下图所示:

MODI26-05

接着,执行下面函数调用语句:

printf(″按公式计算的结果是: %f\n″, fun(m));

调用库函数printf在标准输出设备将计算结果输出,这里printf的实参是函数fun的返回值,必须先调用函数fun并等待fun的返回值。因此,以变量m为实参调用函数fun,于是,main的执行被挂起(暂停),函数fun开始执行。首先,为形参变量m分配存储,完成参数传递:将main中的变量m的值复制于fun的形参m。

在函数fun中,声明:

int i;
double y = 0.5;

定义整型变量i,分配存储,不作初始化,用于for循环控制和生成数列的每一项。定义双精度浮点型变量y,分配存储,初始化为0.5,写作double型常量0.5,y用于从中减去数列的前N项,这种变量通常预先设置初值。至此,当前存储状态如下图所示。形参m也是fun的局部变量。这时有两个变量m,同名同型,但不会冲突,因为它们分属不同的函数fun和main、分别存在于不同的存储空间、各有自己的作用域。局部变量只能在自己的作用域中访问。

MODI26-06

在函数fun中,首先执行下面语句:

for (i = 2; i <= m; i++)
­      y −= 1.0 / ((double)i ∗ (double)(i+1));

for循环过程中,变量i的值不断变化,i依次取值2,3,4,5,6(m+1)。当i取6时,循环因条件i <= m为假而终止。i每取一值(6除外),均执行一轮循环体:语句y −= 1.0 / ((double)i ∗ (double)(i+1));将i的当前值和(i+1)的值都强制转换为double型,得到double型乘积即数列当前项的分母值,求倒数,再从变量y减去该倒数。当for循环终止后,变量y的值0.166667就是所要求的公式结果。注意:(double)i表示对i进行强制类型转换,(double)(i+1)表示对(i+1)进行强制类型转换,(double)叫做强制类型转换操作符。至此,当前存储状态如下图所示:

MODI26-07

紧接着,执行下面语句:

return y;

将所求结果0.166667返回调用者函数main,函数fun执行结束,同时收回其存储空间。

函数fun返回后,main从挂起点即语句printf(″按公式计算的结果是: %f\n″, fun(m));恢复继续执行,将fun的返回值0.166667暂存于临时分配的变量、再作为实参交给库函数printf、在标准输出设备上按默认格式输出。至此,当前存储状态如下图所示:

MODI26-08

这是函数main的最后语句,随即函数main执行结束、同时收回其存储空间。

知识点:

① 程序执行是动态过程,语句不断执行,变量值不断变化,同时伴随函数局部变量(含形参变量)的创生与消亡。当函数进入时,局部变量因自动分配存储而创生;当函数返回时,因自动收回存储而消亡。形参变量经参数传递获得初值且有意义,非形参局部变量若未经显式初始化,则其初值无意义,称为垃圾值,仅反映新分配内存的当前随机性物理状态。参数传递发生在函数进入后、函数的语句开始执行前,实参表达式(含变量)的值被复制于形参变量之中,名曰″传值″。不同函数的局部变量可以同名,不会冲突,驻留各自的存储空间,只能被所属函数自己的语句访问,这就是局部变量的作用域。函数通过对形参指针的间接引用可以访问调用者函数内的变量,是函数之间双向通信的重要方式,既能从调用者接收数据,又能向调用者回送计算结果。函数通过非指针形参只能从调用者函数接收数据,通过函数返回值只能向调用者回送计算结果,都只是单向通信。

② 强制类型转换的语法和语义:

语法格式:

­      (类型名)  被转换表达式

语义:

­      将被转换表达式(含单个变量)的值强制转换为指定类型,圆括号是必不可少的。

例如:

­      语句y −= 1.0 / ((double)i ∗ (double)(i+1));中的(double)i指明将i的值强制转换为double型,(double)(i+1)指明将(i+1)的值强制转换为double型。强制类型转换操作符(double)的优先级高于乘除运算符∗ /,但低于圆括号()。

③ 第1处改错考查:for语句循环条件的确定。应当将for (i = 2; i < m; i++)改为for (i = 2; i <= m; i++)。若循环条件取为i < m,则当i增1后取得m的值时会因i < m为假而导致循环终止,公式的最后一项不会被减去,发生错误;若循环条件取为i <= m,就能保证公式最后一项也被减去,结果正确。考生要斟酌类似细节(包括i初值的确定)。

④ 第2处改错考查:强制类型转换。题干中的公式显然要求实数运算,C语言用浮点运算表示实数运算,有关操作数都必须是浮点类型,浮点运算与整数运算规则不同,结果不同。本题采用双精度浮点double类型,因此应将y −= 1 / (i ∗ (i+1));改为y −= 1.0 / ((double)i ∗ (double)(i+1));,即将int型常量1写作double型常量1.0,将int型的i和(i+1)的值均强制转换为double类型。

注意:其实将y −= 1 / (i ∗ (i+1));改为y −= 1 / ((double)i ∗ (i+1));更简单且正确,int型常量1和int型子表达式(i+1)的值均会自动转换为double型再参与运算。但据分析,标准答案是改为y −= 1.0 / ((double)i ∗ (double)(i+1));,切记!

⑤ 函数实参可以是变量、函数调用、表达式。以下实例都是常见的正确写法:

printf(″%d\n″, x);

printf(″按公式计算的结果是: %f\n″, fun(m));

printf(″The result is: %f\n″, a∗b+10);

⑥ 算法:

先为变量y置初值0.5即首项1/2;再以for循环方式依次生成数列的其余各项并从变量y中逐一减去。关键是确定循环控制变量i的增长规律(包括初值和循环条件的确定)以及i与数列通项的依赖关系,即1 / (i ∗ (i+1))。

⑦ 赋值语句y −= 1.0 / ((double)i ∗ (double)(i+1));的计值过程是:i与1同为int型,执行+操作,结果值为int型;将int型i和(i+1)的值均强制转换成double型,执行∗操作,结果值为double型;1.0与 ((double)i ∗ (double)(i+1))的值同为double型,执行/操作,结果值为double型;y与1.0 / ((double)i ∗ (double)(i+1))的值同为double型,执行−=操作,将1.0 / ((double)i ∗ (double)(i+1))的值从y的当前值减去后再赋于变量y,y的值同时作为整个赋值表达式y −= 1.0 / ((double)i ∗ (double)(i+1))的结果值(double型)。注意:(double)像+  −  ∗  /一样也是一个操作符,只是看着不太习惯。

⑧ 在赋值表达式y −= 1.0 / ((double)i ∗ (double)(i+1))后面添加分号;后,就变成赋值语句y −= 1.0 / ((double)i ∗ (double)(i+1));。其实,在任何表达式后面添加分号后都会变成相应的表达式语句,最常见的例子是:增1表达式++i与相应的增1语句++i;

练习题:

试改变代码行double y = 0.5;和y −= 1.0 / ((double)i ∗ (double)(i+1));中的0.5和(i+1),为自己命新题,举一反三。

返回