K&R 6.3 结构数组与结构指针

  K&R6.3节中,有一个匹配关键词的程序,它的作用是对输入的文本进行关键词次数统计。需要注意的是,结构类型的变量 lib 在初始化时,需要对关键词按顺序进行排序,这是因为在后面用到的二分查找中,由于检索的过程中它是调用了strcmp(*, *)函数,如果关键词是按升序进行的,则在if语言中,strcmp返回大小为两者单词从左至右第一个不相同的字母之差,若为负数,则表明输入单词word与lib中前半部分某个单词相匹配,则将lib中间往前一个的单词赋给右端;如果strcmp为正值,则把lib中间往后一个的单词赋给左端;如果strcmp返回零,则表明单词word和lib中的单词相同,返回该单词在lib中的序号。

这里的strcmp位于标准库string.h中,其函数为

int strcmp(char *s, char *t)
{
    for( ; *s == *t; s++, t++)
        if(*s =='\0')
            return 0;
    return *s - *t;
}
这个函数中for循环不能用while(*s++ ==* t++)替换,因为当不满足*s++==*t++跳出循环时,指针s和t都向比for循环中多向前移动了一位,因此返回的*s - *t 是不正确的,例如当*s="abc“  *t = "acc"时,返回应该时b与c的差值-1,而不是c与c的差值0。
      对于strcmp,有时候希望它只对字符串前n个单词进行比较,则可以这样修改strcmp函数:

int strcmp(char *s, char *t, int n)

{

    char *p=t;

    while(n--)

        t++;

    *t = '\0';

    t=p;

    for(;*s==*t;s++,t++){

        if(*s=='\0')

            return 0;

    }

    return *s-*t;

}

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXWORD 100
#define BUFSIZE 10000
#define NKEYS ( sizeof (lib)/sizeof (lib[0]) )

struct key{
    char *word;
    int count;
} lib[] = {
    "1984",0,
    "apple",0,
    "book",0,
    "bottle",0,
    "box",0,
    "death",0,
    "frank",0,
    "key",0,
    "surface",0,
    "swift",0,
    "tea",0,
    "water",0
};
/* 这里必须按字符顺序排列,因为binsearch中是按strcmp检索的,如果不按顺序排,则检索不到所需关键字的位置 */


int mybinsearch(char *, struct key *, int );
int gainword(char *, int );

int main(int argc, const char *argv[])
{
    int n;
    char word[MAXWORD];
    
    while (gainword(word, MAXWORD)!=EOF)
        if (isalpha(word[0]))
            if((n=mybinsearch(word, lib, NKEYS)) >= 0) //>=0说明在binsearch中能找到匹配单词
                lib[n].count++;
    
    for(n=0; n<NKEYS; n++)
        if(lib[n].count > 0){
            printf("  %d",lib[n].count);
            printf("\t%s",lib[n].word);
            printf("\n");
        }
    return 0;
}

//结构体的二分查找
int mybinsearch(char *word, struct key v[], int n)
{
    int temp;
    int low, high, mid;
    
    low = 0;
    high = n-1;
    while (low <= high){
        mid = (low + high)/2;
        if ((temp=strcmp(word, v[mid].word)) < 0)
            high = mid - 1;
        else if (temp > 0)
            low = mid + 1;
        else
            return mid;
    }
    return -1;
}

//获取字符串
int gainword(char word[], int lim)
{
    int c;
    
    int Getch(void);
    void Ungetch(int);
    char *w=word;
    
    while (isspace(c=Getch()))//如果输入连续多个空格,则c只接受一个空格,并把它保存到w中
        ;
    if (c != EOF)
        *w++ = c; //这里w用来存放除EOF之外的任意字符,但是如果该字符不是字母,则在后面追加‘\0’,病返回该字符
    if (!isalpha(c)){
        *w = '\0';
        return c; //返回EOF或者任何非字母的字符
    }
    for ( ; --lim > 0; w++){
        if (!isalnum(*w = Getch())){ //从缓冲区中读入一个字符,该字符如果不是字母或数字,则压回并跳出循环
            Ungetch(*w);
            break;
        }
    }
    *w = '\0';
    return word[0]; //返回单词的第一个字符
}

//Getch和Ungetch函数:用于将从缓冲区输出与压回
char buf[BUFSIZE];
int bufp = 0;

int Getch(void)
{
    return (bufp>0) ? buf[--bufp] :getchar();
}

void Ungetch(int c)
{
    if (bufp >= BUFSIZE)
        printf("Ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

如果要将上述程序改为结构体的指针形式,可以修改main函数和mybinsearch函数,mybinsearch返回一个结构体指针,指向所匹配的结构体成员或者NULL,修改后的main函数和mybinsearch函数如下,这里需要注意的是这里的high是=指向的是lib[n]而非lib[n-1](这是可以的,但不能间接引用lib[n]),同时,指针之间不能做加法运算,因此mid=(low+high)/2需要变为mid=low+(high-low)/2。

需要特别注意的一点是,由于lib结构体中不同成员的类型不同,有char和int两种形式,但是结构的长度并不等于所有结构体成员的类型长度之和,这是因为结构体中可能会增加一些”填充物“来满足对象之间的对齐要求。

//主函数
int main(int argc, const char * argv[]){
    struct key *p;
    char word[MAXWORD];
    while (gainword(word, MAXWORD) != EOF)
        if(isalpha(word[0]))
            if((p=mybinsearch(word, lib, NKEYS)) != NULL)
                p -> count++;
    
    printf("%s\t%s\n","TIMES", "WORDS");
    for(int i=0;i<15;i++)
        printf("-");
    printf("\n");
    
    for(p= lib; p<lib+NKEYS; p++)
        if(p->count>0)
            printf("%4d\t\t%s\n",p->count, p->word);
    return 0;
}
//mybinsearch函数
struct key * mybinsearch(char *word, struct key *tab, int n){
    int cond;
    struct key *low, *high, *mid;
    low = tab;
    high = low + n;
    while(low < high){
        mid = low + (high - low)/2;
        if ((cond = strcmp(word, mid -> word))<0)
           high = mid;
        else if(cond>0)
           low = mid + 1;
        else
           return mid;
    }
    return NULL;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值