《C和指针》学习笔记,1

最近C语言已经学完,布置的大作业:学生管理系统5个版本也完成了。但是又买了一本《C和指针》,主要是感觉自己的指针还是没有完全熟悉。所以还是要好好研究一下。闲话不多说,直接第一章。一看是快速入门,以为很简单,但那个程序就把我卡了半天才看懂,按照作者说的的确运用了C语言中的大部分技巧。

程序1.1:首先读取一串列标号,这些列标号成对出现,便是输入行的列范围。这串列标号以一个负值结尾,作为结束标志。剩余的输入行被程序读入并打印,然后输入行中被选中范围的字符串被提取出来打印。

书中代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CLOS 20         /*所能处理的最大列号*/
#define MAX_INPUT 1000       /*每个数入行的最大长度*/
int read_column_numbers(int columns[],int max);
void rearrange(char *output,char const*input,int n_columns,int const columns[MAX_CLOS]);
int main(void)
{
   int n_columns;           /*进行处理的列标号*/
   int columns[MAX_CLOS];   /*需要处理的列数*/
   char input[MAX_INPUT];    /*需要容纳的输入的数组*/
   char output[MAX_INPUT];   /*容纳输出行的数组*/

   n_columns=read_column_numbers(columns,MAX_CLOS);
   while(gets(input)!=NULL) /*如果读入不存在输入行则程序结束*/
   {
       printf("Original input :%s\n",input);
       rearrange(output,input,n_columns,columns);
       printf("Rearranged line :%s\n",output);
   }
   return EXIT_SUCCESS;
}

/*读取需要处理的列标号,如果超出规定范围不予理会*/
int read_column_numbers(int columns[],int max)
{
    int num=0;
    int ch;
    while(num<max&&scanf("%d",&columns[num])==1&&columns[num]>0)
    {
        /*这个循环条件保证了读取的行号不会超过最大值,而且利用&&的短路特性,
          也不会即使超过最大行号也不会被scanf读入,同时scanf保证了读入整型数据,
          同时后面的条件输入的为正数*/
          num+=1;
    }
    /*判断是否读入的数据是成对的*/
    if(num%2)
    {
        puts("Last column number is not paired.");
        exit(EXIT_FAILURE);
    }
    while((ch=getchar())!=EOF&&ch!='\n');  /*用来处理包含最后那个负值的所有字符*/
    return num;
}
/*处理输入行*/
void rearrange(char *output,char const*input,int n_columns,int const columns[MAX_CLOS])
{
    int col;                                            /*columns数组的下标*/
    int output_col;                                     /*输出行的列计数器*/
    int len;                                            /*输入行的长度*/
    int nchars;                                         /*成对处理的列之间的长度*/
    len=strlen(input);
    output_col=0;
    for(col=0;col<n_columns;col+=2)
    {
        if(columns[col]>=len||output_col==MAX_INPUT-1)  /*如果输入行的标号小于需要处理的列标号或者
                                                          输出数组已满结束任务*/
            break;
        nchars=columns[col+1]-columns[col]+1;
        if(output_col+nchars>MAX_INPUT-1);
            nchars=MAX_INPUT-output_col-1;              /*如果输出行数据空间不够则只处理到能容纳到的数据*/
        strncpy(output+output_col,input+columns[col],nchars);
        output_col+=nchars;
    }
    output[output_col]='\0';
}

一点点思考:不得不说,Kenneth这种大师思维的确很完善,要是我写的确很多地方都想不到(我怎么敢和大师比2333!),比如while(ch=getchar())那个语句,一开始我百思不得其解,后来才明白为的是清除负值之后的字符(包括负值本身)因为这些字符虽然没有被读走,同时也仍然在stdin中存在。所以在主函数调用gets这个函数进行读取的时候这些未被读走的字符就会被解释为第一行数据进而使我们得不到自己想要的结果。 而他对于可能造成数组越界访问的情况也进行了处理。想想自己在写的时候从来没有在乎过这些,都是越界了才会debug找出来。另外一个细节就是他传入rearrange这个函数的input数组,加了前缀const,防止函数内可能造成的改动。这点也是我需要学习的。

不过我也对于他的主函数设置的while条件有一些异议,gets函数在读到文件尾(EOF)才会得到NULL,这种的确适合我们打开一个文件进行读取的操作,因为这个文件的末尾一定会有EOF。但是如果我们在键盘输入的时候,我们只有CtrlZ才可以得到EOF,这点总让我感觉很不爽,其实主要是我懒不想摁CtrlZ,另外是我在书上并不是很理解它的输入样例(大家可以去看一下),里面没有CtrlZ,所以,我并不是很懂作者的意思。但貌似没有其他更好的方法了。就暂且认为是需要输入CtrlZ。在这个问题上纠结这么久,我也是醉了。

这个程序的确很经典,包含的东西多但不难,其实大家可以按照示例试一下输出,然后就会得到很好玩的结果了。感觉自己学了一学期C,还是什么都不会,自己打的时候打不下去,然后看了书的程序也是看了半天。。。可能是因为最近在看Java导致的吧。不能一天放松啊。。。