目录
题目一:
编写一个程序,在遇到EOF之前,把输入作为字符流读取。该程序要报告平均每个单词的字母数。不要把空白统计为单词的字母。实际上,标点符号也不应该统计,但是现在暂时不同考虑这么多(如果你比较在意这点,考虑使用ctype.h系列中的ispunct()函数)。
这是《C Prime Plus》一书中的题8-4,做题前,我们有必要了解一下EOF是什么意思。
EOF是end of file的缩写,表示"文字流"(stream)的结尾。这里的"文字流",可以是文件(file),也
可以是标准输入(stdin)。
EOF在<stdio.h>中是被定义的一个常量:
#define EOF (-1)
而第二个概念,字符流是IO流的一种,计算机通过IO流来实现对硬盘文件的读和写操作;但我们的
输入一般是通过键盘键入缓冲区存放的,实现的是对缓冲区的写入操作。
流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。
“把输入作为字符流读取”,这里只是利用一种概念上的相似性完成程序。
现在讲回题目,一般两个词之间我们用一个空格隔开,但最后一个单词我们一般用标点并不用空格
作为结尾,也就是说,单词数=空格数+1,空格数可以用变量sp_iNum表示,即:
int sp_iNum=0;//int sp_iNum={0};
。然后,我们需要记录得到的字母总数,定义一个变量:
int iNum=0;//int iNum={0};
{0}是一个可以给很多种变量初始化的东西,
例如:
char cChar={0};
char cChar[10]={0};
char *file={0};//即char *file=NULL;
接下来,我们定义一个用于接收并暂存字符供检验的容器(一次检验一个字符):
char cChar;
现在我们不将标点统计在总字母数iNum里,那我们就要用到<ctype.h>中的ispunct()函数,
返回值为非零(真)表示
c
是标点符号,返回值为零(假)表示c
不是标点符号。
#include<ctype.h>
#include<stdio.h>
int main()
{
char cChar;
cChar = '?';
int iChar = ispunct(cChar);
printf("%d", iChar);
return 0;
}
结果为:
解法一:
#include<stdio.h>//8-4
#include<ctype.h>
int main()
{
char cChar = 0;
int iNum = 0;
int sp_iNum = 0 ;
while ((cChar = getchar()) != EOF)
{
if (cChar == ' ')
sp_iNum++;
else if (ispunct(cChar))
;
else if(cChar != ' '&&!ispunct(cChar)&& cChar != '\n')
iNum++;
else if (cChar == '\n')
{
printf("%.2f\n", iNum*1.0 / (sp_iNum + 1));
iNum = 0;
sp_iNum = 0;
}
}
}
Where there is a will,there is a way!
这句话共有28个字母,空格数有7个,故每个单词平均有个。在我看来,难理解的应
该是我输入了一整句话,为什么不是只有第一个字母'W'才能被赋给cChar呢?
getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了,第一次调用getchar()时,确实需要人工的输入,但是如果你输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取了。
“以后的getchar()再执行时就会直接从缓冲区中读取了”,这句话就是原因啦。这段话也可以这么理
解:我提前将要装进容器cChar的字符一个个在缓冲区先放好,在第一个字母'W'被装进去并检验后,再将'W'倒掉,然后是'h',接着是'e'······直到最后一个字符。
解法二:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int ch, i = 0, j = 0;
float sum;
printf("Please enter a word the inspection of the average number of letters and words several letters\n");
printf("After input press CTRL + Z to calculate:\n");
while ((ch = getchar()) != EOF)
{
if (ch >= 'a'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z')
i++;
else if (ch == 32)
j++;
}
sum = i*1.0 / (j + 1);
printf("Letters have %d\naverage number of letters have %0.2f\n", i, sum);
return 0;
}
原文为CSDN博主「一线生」的原创文章,上面的代码较原文有改动,原文链接。
原文代码有些小错误,如:
if (ch >= 'A'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z')
//应为if (ch >= 'a'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z')
sum = i / (j + 1);//可为sum = i *1.0/ (j + 1);
否则在等式右边的式子自动取整后,才会将值拷贝给sum转换为float类型。
若不改动,设i=11,j=2,则sum=3,而不是6.67
Ctrl+z是将任务中断,挂起的状态。
具体作用建议看这篇博文:Ctrl+z跟Ctrl+c的区别_绮梦寒宵的博客-CSDN博客_ctrl+z和ctrl+c
题目二:
求n的阶乘。
题目很容易理解,n的阶乘N=1*2*······*(n-1)*n(n>=1)。
解法一:
#include<stdio.h>
int main()
{
int n,fact=1;
scanf_s("%d", &n);//Dev-C++:scanf("%d", &n);
while (n > 1)
fact *= n--;
printf("%d", fact);
return 0;
}
下面讲解一下这个程序:fact用来存储最终的结果,n即要用来计算阶乘的数字,其中fact要赋予初值为1。
while (n > 1) //while (n >=1)也可以
fact *= n--;
fact与未进行自减操作的n相乘,并将结果拷贝到fact中,取代fact中原先的值。
结束的临界条件为n=2,执行完n=2这次后不再执行 fact *= n--;
即fact=2*······*(n-1)*n,若你为n赋值为1,将直接打印出fact的初始值1。
那么我们再想一下如何利用递归函数来解决这个问题:
解法二:
#include<stdio.h>
int facto(int n)
{
int fact = 1;
if (n > 1)
fact = facto(n - 1)*n;
return fact;
}
int main()
{
int n,fact;
scanf_s("%d", &n);//Dev-C++:scanf("%d", &n);
fact = facto(n);
printf("%d", fact);
return 0;
}
难点在于这个facto()函数,其中的语句:
if (n > 1)
fact = facto(n - 1)*n;
正是facto()递归地调用自身。
没学过递归的同学看这个可能有点懵,不要紧,让我细细道来,帮你开个头。
在上面的语句中,facto(n-1)意即将n-1(n-1>0的情况下)这个值通过这个facto()函数继续过一遍,
这样的套娃操作会在n=1时结束,届时,会从最小的那个娃娃开始反套娃。
最小的套娃是那个呢?是这个:
facto(1)
{
int fact = 1;
if (n > 1)//n=1不执行这两行
fact = facto(n - 1)*n;
return fact=1;
}
倒数第二小的是:
facto(2)
{
int fact = 1;
if (n > 1)
fact = facto(1)*2=1*2;//注意,代码不能这么写,这样写只是方便讲解。
return fact=1*2;
}
倒数第三层是:
facto(3)
{
int fact = 1;
if (n > 1)
fact = facto(2)*3=1*2*3;
return fact=1*2*3;
}
倒数第四层:
facto(4)
{
int fact = 1;
if (n > 1)
fact = facto(3)*4=1*2*3*4;
return fact=1*2*3*4;
}
就这样,我们一层层套到最大的娃:
facto(n)
{
int fact = 1;
if (n > 1)
fact = facto(n - 1)*n=1*2*3*4*······*(n-1)*n;
return fact=1*2*3*4*······*(n-1)*n;
}
然后我们将用下面的返回语句将值拷贝给main()函数中的变量fact
return fact=1*2*3*4*······*(n-1)*n;
最终套娃结束,随着打印出fact的值,程序结束。
注:由于两个fact变量定义的地方不同,所以它们的作用域也不同,不用担心它们会起冲突
通过这个例子,我们可以归结出递归的一般思想:通过不断地求取某种极限,我们可以得到最底层
返回的条件,最终一层层倒退回来。
当然,这只是个人理解,不妥还请读者指正。
我的上一篇博客:一题多解×1
我的下一篇博客:结构体×共用体×枚举类型