第17题  将字符串最右边的n个字符复制并逆置构成新串

本题程序中,函数fun通过字符指针形参s接收一个字符串,通过字符指针形参 t 接收一个空的字符数组,通过形参n接收一个整数,把字符串s最右边的n个字符复制到t 中、再将其逆置构成一个新串。若s的长度小于n,则将整个s复制到t中再逆置。

例如:若s接收字符串″as2d67rs3t58p109″,n接收整数10,则t中的新串应为″901p85t3sr″

请在程序的下划线处填入正确内容并把下划线删除,使程序得出正确结果。注意:源程序存放在考生文件夹下的BLANK1.C中。 不许增行或删行,也不许更改程序结构。

BLANK1.C:

BLANK17-01

参考答案:

BLANK17-02

运行结果:

BLANK17-03

程序解析:

设想C概念机执行源程序。操作系统平台环境(如Windows)加载程序、调用函数main使之开始执行。首先,声明

char s[N], t[N];
int n;

定义长度为N即100的字符数组s和t,下标从0到99,可容纳100个字符,分配存储,但未作初始化。定义int变量n,分配存储,但未作初始化。凡未经初始化的变量,其初值无意义,仅反映新分配内存的当前随机性物理状态。执行至此,当前存储状态如下图所示,变量的无意义垃圾值以空白表示。注意:数组名s和t本质上是指向首元素的常量指针,常量指针只供引用但不能被赋值。100个字符连续顺次占据一段连续内存。可用下标式s[i]或指针式∗(s+i)访问s的第i个字符元素。

BLANK17-04

首先,执行下面两条库函数调用语句:

printf(″请输入一个字符串:″);
gets(s);

在标准输出设备(通常是显示器)上,给出输入提示信息,再从标准输入设备(通常是键盘)读取一个文本行(字符串)于数组s中,行结束符被空字符′\0′取代。要正常调用库函数printf和gets,就必须前置指令行#include <stdio.h>以包含必要的支持信息,该指令通常写在源文件开头处。执行至此,当前存储状态如下图所示。

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

printf(″请输入整数n:″);
scanf(″%d″, &n);

与上面类似,给出输入提示,读取一个整数于变量n中。要调用库函数scanf,也必须前置指令行#include <stdio.h>以包含必要信息,执行至此,当前存储状态如下图:

BLANK17-05

接着,执行函数调用语句fun(s, t, n);以变量s、t、n为实参调用函数fun,于是,main的执行被挂起(暂停),函数fun开始执行。首先,为形参变量s、t、n分配存储,完成参数传递:将main中的数组名s和t的值传值(拷贝)于fun的形参s和t,即让fun的指针s和t分别指向main的数组s和t的首元素,以便通过fun的指针s和t也可访问main的数组s和t。将main中的变量n传值(拷贝)于fun的形参变量n。形参声明char ∗s定义s为字符指针。声明int i, j, len;定义变量 i、j、len,分配存储,但未作初始化。i、j、len都是fun的局部变量。执行至此,当前存储状态如下图所示。形参s和t也是fun的局部变量。注意:这里有两个变量s、两个变量t、两个n,同名同型、但都不会冲突,因为它们分属不同的函数fun和main、分别存在于不同的存储空间、各有自己的作用域。fun的局部变量只能在fun中访问。

BLANK17-06

接着,执行下面两条赋值语句:

j = 0;
len = strlen(s);

j = 0;使j清零,指示字符数组t的第一个空闲元素的下标; len = strlen(s);调用库函数strlen求得串s的长度并赋值于变量len,均为复制新串做准备。接着执行if语句:

if (n >= len)
­     strcpy(t, s);
else {
­     for (i = len−n; i < len; ++i)
­          t[j++] = s[i];
­     t[j] = ′\0′;
}

若指定的n值大于等于串s的长度,则调用库函数strcpy将串s拷贝于字符数组t之中;若指定的n值小于串s的长度,则执行for循环语句:

for (i = len−n; i < len; ++i)
­      t[j++] = s[i];

将串s中的len−n即下标6至串尾的10个字符逐个复制到字符数组t之中。在这个循环复制过程中,每复制一个字符后、即令j加1以指示下一个空闲位置。紧接着,由语句 t[j] = ′\0′;设置串尾标记。执行至此,复制完毕,当前存储状态如下图所示:

BLANK17-07

接着,执行下面语句:

i = 0;
j = strlen(t)−1;
while (i < j) {
­      len = t[i];
­      t[i] = t[j];
­      t[j] = len;
­      ++i, –j;
}

将字符串 t 就地逆置。执行至此,当前存储状态如下图所示:

BLANK17-08

由于再无其它语句,因此,函数fun即刻返回main(无返回值)、同时收回fun的存储空间、main从挂起点即函数调用语句fun(s, t, n);恢复继续执行,执行下面两条语句:

printf(″结果串为:″);
puts(t);

给出提示,并调用库函数puts将新串t输出。执行至此,函数main执行结束、收回其存储空间、同时将程序执行的控制权归还操作系统环境。

知识点:

① C概念机是指在概念上执行C程序的抽象机器。实际上,C程序要经过预处理、编译、连接等处理环节,最终被翻译成能在特定操作系统平台运行的应用程序。能完成这种翻译的程序套件叫做C的一个具体实现,例如:微软的VC2010就是基于Win32平台的ANSI−C的一个实现。当一个被实现的C应用程序在其目标平台实际运行时,C程序员在头脑中认为是C程序的源代码被一台抽象机直接解释执行,称为C概念机。

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

③  fun的形参声明char ∗s,声明s为字符指针,可用来接收并访问字符数组,如果确实接收了数组,则可采用下标式s[i]或指针式∗(s+i)访问第i个字符元素。

④ 函数通过指针形参可访问调用者的变量,是被调函数与调用函数之间交换数据的重要途径。本题函数fun用指针s和t访问main的数组s和t。

⑤ 要正常调用库函数strlen和strcpy,就必须前置指令行#include <string.h>以包含必要的支持信息,该指令通常写在源文件开头处。

⑥ 若函数形参是指针变量,则其对应实参可以是变量的地址,也可以是数组名,因为指针变量的内容就是地址,而数组名是指向数组首元素的指针,即首元素的地址。例如:本题函数fun的形参声明是fun(char ∗s, char ∗t, int n),而main中的函数调用是fun(s, t, n);,其中char ∗s和char ∗t,声明s和t为字符指针,其对应实参是main中的数组名s和t,二者都是指针。

⑦  ′\0′ 的数值的确是 0,之所以写成 ′\0′ 是为了强调其字符本性。当它在表达式中出现时,其类型为 int;当它在字符串中被存储时,其类型为 char

⑧ 数组s的下标范围从0至N−1,s中字符串的下标范围从0至len,len是s的串长,下标len对应的字符是串尾标记即空字符′\0′,因此s最右边n个字符的下标范围是从len−n至len−1(不包含串尾标记′\0′),所以fun中的for循环写作:

for (i = len−n; i < len; ++i)
­      t[j++] = s[i];

将串s最右边n个字符复制到t中(不包含串尾标记′\0′),再由语句 t[j] = ′\0′;单独设置串尾标记′\0′。i < len也可写作i <= len−1。

⑨ 在构造或复制字符串时一定不要忘记设置串尾标记′\0′,这个串尾标记′\0′有时由库函数自动设置,例如,库函数gets,有时由程序员显式设置,例如fun函数。库函数gets从标准输入设备(通常是键盘)读取一个文本行,并以′\0′取代尾部换行符′\n′

⑩ 库函数char ∗strcpy(char ∗s, char ∗ct)将串ct复制到字符数组s之中,包括串尾标记′\0′

练习题:

试按自己的想法改写程序,为自己命新题,举一反三。

返回