第16题  将字符串中各英文字母变为其前趋字母

在本题程序MODI1.C中,函数fun通过字符指针形参s接收一个字符串,将各英文字母变为其前趋字母,即z变为y、y变为x、…、a变为z,Z变为Y、Y变为X、…、A变为Z,其它字符一律不变。

例如:若接收串为″ABCD12efgh34#%!$Zz″,则结果应为″ZABC12defg34#%!$Yy″

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

MODI1.C:

MODI16-01

参考答案:

MODI16-02

运行结果:

MODI16-03

程序解析:

#include <stdio.h>
#include <ctype.h>

#define N 100

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

宏定义预处理指令#define N 100定义标识符N为字符序列100,在程序下文中,N的每次出现均被替换成100。例如:main中的声明char s[N];等价于char s[100];。

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

char s[N];

定义长度为N即100的字符数组s,分配存储,未作初始化,s用于接收原始字符串、同时存储结果字符串。凡未经初始化的变量,其初值无意义,仅反映新分配内存的当前随机性物理状态。至此,当前存储状态如下图所示,变量的无意义垃圾值以空白表示。注意:数组名本质上是指向数组首元素的常量指针,常量指针只供引用但不能被赋值,其存储空间是在有关表达式计值过程中临时自动分配的,并不是数组存储块的组成部分。因此,s是字符指针。由于机器内存是一维连续字节序列的本性,数组元素顺次占据一段内存。数组下标从0开始,例如:s是指向第0元素的指针,通过s[i]或∗(s+i)可访问数组s的第i元素。若p为字符指针,则通过∗p可访问p所指向的字符。

MODI16-04

首先,执行下列语句:

printf(″请输入一行字符:\n″);
gets(s);

printf语句在标准输出设备(通常是显示器)给出提示,gets语句从标准输入设备(通常是键盘)读取两行字符(以换行符′\n′结尾)、存储于字符数组s中、并以空字符′\0′取代换行符。至此,当前存储状态如下图所示。

MODI16-05

然后,执行函数调用语句:

fun(s);

以数组名s(自动转换为字符指针)为实参调用函数fun,于是,main的执行被挂起(暂停),函数fun开始执行。首先,为形参变量s分配存储,完成参数传递:将main中的数组s的第0元素的地址值(即指向第0个元素的指针)传值(拷贝)于fun的形参s,即让fun的指针s指向main中的数组s的第0元素,以便通过fun中的指针s来访问main中的数组s。至此,当前存储状态如下图所示。形参s也是fun的局部变量。这里有两个变量s,同名同型,但不会冲突,因为它们分属不同的函数fun和main、分别存在于不同的存储空间、各有自己的作用域。局部变量只能在其所属函数中访问。

MODI16-06

在函数fun中,执行其唯一语句即可完成任务:

while (∗s != ′\0′) {
­      if (isalpha(∗s))
­            if (∗s == ′A′)
­                  ∗s = ′Z′;
­            else if (∗s == ′a′)
­                  ∗s = ′z′;
­            else
­                  −−∗s;
­      s++;
}

语句结构:while循环体由一条if语句(下辖一个三路选择else-if结构)和一条增1语句s++;组成。while循环以指针s扫描整个字符串,语句s++;驱使指针s依次扫过每个字符,当s所指字符∗s为′\0′时循环终止,因此循环继续的条件为∗s != ′\0′,即指针s所指字符∗s不是′\0′,∗s != ′\0′等价于∗s != 0,还等价于∗s,优选答案为∗s != ′\0′。在while循环过程中,每当指针s指向一个字符∗s,均以被指字符∗s为实参调用库函数isalpha判别∗s是否英文字母,若isalpha返回真,则表明∗s是英文字母,随即执行三路选择else-if结构,进一步区分∗s是大写′A′、小写′a′、还是′A′′a′以外的其它字母:若∗s是大写′A′则执行∗s = ′Z′;将其变为大写′Z′;若∗s是小写′a′则执行∗s = ′z′;将其变为小写′z′;若∗s是′A′′a′以外的其它字母则执行−−∗s;将其变为前趋字母。注意:−−∗s;等价于−−(∗s);,因为运算符−−和∗优先级相同且服从自右而左的结合性,即令被指字符的码值∗s减1,变为其前趋字母的码值。当while循环终止后,函数main中的字符串s就在原地完成转变,整个转变过程是通过fun的指针s实现的。这里的同名指针s不会冲突,因为它们分属不同的函数fun和main、分别存在于不同的存储空间、各有自己的作用域。至此,当前存储状态如下图所示。

MODI16-07

由于这是函数fun的唯一语句,且全部任务已完成,fun返回调用者main,但无返回值,同时收回fun的存储空间。函数fun返回后,main从挂起点即语句fun(s);恢复继续执行,函数fun虽无返回值,但其执行效果已通过指针保留在main的数组s中。至此,当前存储状态如下图所示。

MODI16-08

在函数main中,接着执行下列语句:

printf(″结果串如下:\n″);
puts(s);

将结果字符串s在标准输出设备(通常是显示器)上输出。由于这是main的最后语句,随即函数main执行结束、同时收回其存储空间。

知识点:

① 组成C程序的各个函数自己的局部变量(含形参变量)在函数进入后、函数的语句开始执行前,自动分配存储空间,并以″传值″方式(即拷贝复制)完成参数传递,这些局部变量的存储空间在函数返回的同时自动撤销回收。不同函数的局部变量可以同名,不会冲突,驻留各自的存储空间,只能被本函数自己的语句访问。函数的局部变量,只要未被显示初始化,则其初值无意义,只反映新分配物理内存的当前随机状态。

② 字符数组与字符串的关系。字符数组是用来存储字符数据的,每个元素可以存放机器字符集的任一字符。长度为n的字符串是长度为n+1的字符数组,最后一个字符必须是空字符′\0′,空字符′\0′不计入字符串长度,这是C语言的规定,供程序检测是否到达串尾之用。长度为n的字符数组,可以容纳n个任意字符,因此可以存储长度小于或等于n−1的任意字符串。

③  在C语言中,逻辑表达式的结果类型为int,结果值只取0或1,结果取0表示逻辑假,结果取1表示逻辑真。充当whileforif等语句的条件表达式,可以是任何数值型表达式,包括字符型、整型、实型,其值为0视为逻辑假,其值非0视为逻辑真。

④ 设有字符指针p,且已指向字符数组内某字符元素,那么,++p使p指向下一字符元素,而−−p使p指向上一字符元素。

⑤  设有字符指针变量p,且已指向某字符变量,则可通过∗p间接访问p所指的字符。MODI3-08在这种情形下,可通过∗p间接访问p所指的字符′A′,即字符变量∗p的值是′A′,注意:p和∗p是两个不同类型的变量,p的类型是字符指针类型,即char ∗类型,而∗p的类型是字符类型,即char类型。

⑥ 凡字符串处理程序,通常采用字符指针扫描字符串的方法解决问题。扫描终止的标志就是遇到串尾标记空字符′\0′,而空字符′\0′的机内码值的确为0。像′\0′′\n′′a′等字符常量以及其它字符变量,当出现在表达式中时,自动转换为int类型。

⑦  库函数char  ∗gets(char  ∗s),需要头文件stdio.h支持。

从标准输入设备(通常是键盘),读取下一输入文本行于字符数组s中,并用空字符′\0′替代换行符′\n′,返回s。若遇文件尾或发生错误,则返回NULL。

⑧ 库函数int  puts(const  char  ∗s),需要头文件stdio.h支持。

向标准输出设备(通常是显示器),输出字符串s的内容之后,再输出换行符′\n′。若发生错误,则返回EOF,否则返回一个非负值。

⑨ 库函数int  isalpha(int c),需要头文件ctype.h支持。

若c是英文字母,则返回一个非0值,表示逻辑真;否则返回0,表示逻辑假。在ASCII字符集中,isalpha(∗s)等价于∗s>=′A′ && ∗s<=′Z′ || ∗s>=′a′ && ∗s<=′z′,因为大写英文字母或小写英文字母在ASCII字符集中都是连续排列的(码值连续)。

练习题:

试改变代码

­            if (∗s == ′A′)
­                  ∗s = ′Z′;
­            else if (∗s == ′a′)
­                  ∗s = ′z′;
­            else
­                  −−∗s;

中的适当内容,为自己命新题,举一反三。

返回