今天闲来无事,感觉自己又在虚度光阴,于是就打开了14届的蓝桥杯b组赛的第一题,想要刺激刺激自己懈怠的神经,拿到题一看,是一道填空题,只允许在空中添上应该填的那部分代码,不允许多一点改动,看到题,我想杀人,我承认,我有点暴力倾向,但请相信,我是一个温柔的人+_+。

试问我为何想要杀人,请看题如下:

输入一个字符串,求它包含多少个单词。单词间以一个或者多个空格分开。第一个单词前,最后一个单词后也可能有0到多个空格。比如:” abc xyz” 包含两个单词,”ab c xyz ” 包含3个单词。如下的程序解决了这个问题,请填写划线部分缺失的代码。注意:只填写划线部分的代码,不要填写任何多余的内容。比如已经存在的小括号,注释或说明文字等。

int get_word_num(char* buf)
 {
    int n = 0;
    int tag = 1;
    char* p = buf;

    for(;*p!=0 && *p!=13 && *p!=10;p++){
 if(*p==' ' && tag==0) tag=1;
 if( _____________________ ) { n++; tag=0; }   //填空
    }

    return n;
 }

 int main()
 {
    char buf[1000];
    fgets(buf,1000,stdin);

    printf("%d\n", get_word_num(buf));
    return 0;
 }   

这不是坑我么?难道真是我见识短浅,连个#include<stdio.h>都没有,后面调用的函数怎么可能管用?结果也果真不出我的意料,我都编译器一直对‘stdin’报错,根本无法通过,我只想说,这时哪的盗版题,真能恶心我,百般无奈,只好擅作主张在前面自己加了一个头文件,才总算是可以了。更改后代码如下:

#include <stdio.h>
int get_word_num(char* buf)
{
    int n = 0;
    int tag = 1;
    char * p = buf;
    for(;*p!=0 && *p!=13 && *p!=10;p++)
    {
        if(*p==' '&&tag==0)
            tag=1;
        if(______________)//填空
        {
            n++;
            tag=0;
        }
    }
    return n;
}

int main()
{
    char buf[1000];
    fgets(buf,1000,stdin);
    printf("%d\n", get_word_num(buf));
    return 0;
}    

一看到这个问题,我先找到主函数,分析主函数的结构,里面值得说道的是“fgrts”函数,函数原型“char *fgets(char *buf, int bufsize, FILE *stream);”这个函数有三个参数,分别是:
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明存储数据的大小。
*stream: 文件结构体指针,将要读取的文件流。

具体到这道题,“buf”是指向一个字符数组的地址,是需要从键盘得到的信号源,“1000”是数组的数据上限,“stdin”是#inclede<stdio.h>里的一个函数,stdin是标准输入 std即standard(标准),in即input(输入),合起来就是标准输入。 一般就是指键盘输入到缓冲区里的东西,结合在一起作用就是从键盘上得到一串字符,做后期处理。

然后去分析的自定义整型函数“get_word_num”,参数是一个指针,指向字符数组地址,然后我开始分析这个函数的内部构造,发现了一个不明就里的东西“tag”,我是英语渣渣,一下子根本无法意会他定义这个函数的意义,然后机智的我想到他们每定义一个变量的命名都和他的作用相关联,于是果断去百度了一下,接下了是英语角:
tag——标签,标签——tag,请大家跟我一起读,t,a,g,泰哥,标签……英语角结束,Y(^_^)Y。

然后我就顿悟了一个问题,根据题意,必然需要一个变量去判断字母前是否是空格,否则将无法判断它是否是一个单词,于是“tag”应运而生,如果是并且原来tag标记为不是,则说明原来前面有字母,而现在这个地址是空格,说明是前面的一个单词结束了,则将“tag”标记为1,预示着下一个新的单词出现时应用,那么怎样才说明下一个新的单词出现了呢?很简单,只需要判定到下一个地址的字符不为空格就好了,因为已经标记为1了,这样自然就轻轻松松的判断出有几个单词了,当然这个判断需要在一个循环内部做循环判断,这样才能得到有几个单词,符合判断的在第二个判断语句中将单词数加一,并复原标签为0,即意味着这个单词开始了,还没有结束,一直到这个单词结束不会再有新的单词出现,周而复始,想要达到题的目的就轻而易举了!(这个循环很有意思,和平时见的不太一样,如是“for(;*p!=0 && *p!=13 && *p!=10;p++)”,他循环判断的第一部分和第三部分都是常见的,只是第二部分要求不等于三个值,这三个值在ASCII表中分别表示NUL空字符,CR回车键,LF换行符,这样子也就好理解了,但是依个人的观点,这个限制范围有点问题,最起码需要有一个上限,使p<=1000,这样子应该会使代码更加完美,更加合理,避免出现不必要的问题)

综上所述,最后答案结果为:*p!=' '&&tag==1

整体代码如下:

#include <stdio.h>
int get_word_num(char* buf)
{
    int n = 0;
    int tag = 1;
    char * p = buf;
    for(;*p!=0 && *p!=13 && *p!=10;p++)
    {
        if(*p==' '&&tag==0)
            tag=1;
        if(*p!=' '&&tag==1)//填空
        {
            n++;
            tag=0;
        }
    }
    return n;
}

int main()
{
    char buf[1000];
    fgets(buf,1000,stdin);
    printf("%d\n", get_word_num(buf));
    return 0;
}    
本代码在Mac系统下的Xcode编译器中编译正确,故答案正确。
另外,补充部分知识点:!

ASCII码表部分特殊值