《C和指针》快速入门的例子分析及其扩展

        如果想深入理解C语言的指针,很多人会推荐你读《C和指针》。

        本书在第一章给出了一个快速入门的例子,虽然作者的目的是让读者通过这个例子快速的了解C语言的语法情况,但是这个例子本身的处理逻辑、尤其是一些细节,还是比较晦涩难懂(尽管个人觉着这是一个不错的例子),希望这篇文章能够在逻辑上给需要的人提供一些帮助。至于语法方面,这里不再做解释,如果有需要的话,还是去读原书吧,书上比较详细。最后再总结一下来自练习题的几个扩展。

        话不多说,下面介绍这个程序:先输入字符串,然后再输出特定的一部分;截取的规则是通过下标,前后都包括,具体的范围在第一行的时候输入。下面是程序运行的过程示例:

运行结果:

4 9 12 20 -1
abcdefghijklmnopqrstuvwxyz
Original input : abcdefghijklmnopqrstuvwxyz
Rearranged line: efghijmnopqrstu
Hello there, how are you?
Original input : Hello there, how are you?
Rearranged line: o ther how are
I am fine, thanks.
Original input : I am fine, thanks.
Rearranged line:  fine,hanks.
See you!
Original input : See you!
Rearranged line: you!
Bye
Original input : Bye
Rearranged line:

说明:第一行输入的一串数字是指定截取字符串的范围,通过列标号指定,最后的负数是结束的标志,即”4 9 12 20 -1”表示从第4列到第9列(注:这里的范围和我们常见的前闭后开区间不一样,平时我们说的范围是包括前面但不包括后面,这里是前后都包括,即第4列和第9列也包括,一共有(9-4+1)=6列),第12列到第20列(也是前后都包括,共(20-12+1)=9列)。
首先输入26个字母做了测试,截取第4列到第9列,第12列到第20列,然后输出。这里的列是从0开始计数的(和字符数组的计数方式相同)。

程序代码:

/*
 This program reads input lines from the standard input and prints
 ** each input line, followed by just some portions of the lines, to
 ** the standard output.
 **
 ** The first input is a list of column numbers, which ends with a
 ** negative number.  The column numbers are paired and specify
 ** ranges of columns from the input line that are to be printed.
 ** For example, 0 3 10 12 -1 indicates that only columns 0 through 3
 ** and columns 10 through 12 will be printed.
 
 输入一个字符串,并打印出来,然后再打印出字符串的特定的一部分 

 第一行输入几个列标号,列标号成对出现,其最后一个为负数(用来标识列标号已经输入完毕)
 例如:输入 0 3 10 12 -1 ,标识将字符串的第0列到第3列,第10列到第12列,之间的内容打印出来 

 注:这里输入的列标号从零开始计数,而且是前后都包括(比如:0 3 是包括第0列到第3列,一共4列,不是3列) 
 */

#include 
#include 
#include 
#define	MAX_COLS	20	/* max # of columns to process */
#define	MAX_INPUT	1000	/* max len of input & output lines */

int read_column_numbers(int columns[], int max);
void rearrange(char *output, char const *input, int n_columns,
		int const columns[]);

int main(void) {
	int n_columns; /* # of columns to process */
	int columns[MAX_COLS]; /* the columns to process */
	char input[MAX_INPUT]; /* array for input line */
	char output[MAX_INPUT]; /* array for output line */

	/*
	 ** Read the list of column numbers
	 */
	n_columns = read_column_numbers(columns, MAX_COLS);

	/*
	 ** Read, process and print the remaining lines of input.
	 */
	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;
}

/*
 ** Read the list of column numbers, ignoring any beyond the specified
 ** maximum.
 */
int read_column_numbers(int columns[], int max) {
	int num = 0;
	int ch;

	/*
	 ** Get the numbers, stopping at eof or when a number is < 0.
	 */
	while (num < max && scanf("%d", &columns[num]) == 1 && columns[num] >= 0)
		num += 1;

	/*
	 ** Make sure we have an even number of inputs, as they are
	 ** supposed to be paired.
	 */
	if (num % 2 != 0) {
		puts("Last column number is not paired.");
		exit(EXIT_FAILURE);
	}

	/*
	 ** Discard the rest of the line that contained the final
	 ** number.
	 */
	while ((ch = getchar()) != EOF && ch != '/n')
		;

	return num;
}

/*
 ** Process a line of input by concatenating the characters from
 ** the indicated columns.  The output line is then NUL terminated.
 */
void rearrange(char *output, char const *input, int n_columns,
		int const columns[]) {
	int col; /* subscript for columns array */
	int output_col; /* output column counter */
                    /*注:output_col的值等于strlen(output),即output数组的长度*/
	int len; /* length of input line */
             /*注:len的值等于strlen(input),即input数组的长度*/
	len = strlen(input);
	output_col = 0;

	/*
	 ** Process each pair of column numbers.
	 */
	for (col = 0; col < n_columns; col += 2) {
		int nchars = columns[col + 1] - columns[col] + 1;

		/*
		 ** If the input line isn't this long or the output
		 ** array is full, we're done.
		 columns中指定的范围大于inpput数组的长度, 或者output数组已满, 这时循环结束 
		 */
		if (columns[col] >= len || output_col == MAX_INPUT - 1)
			break;

		/*
		 ** If there isn't room in the output array, only copy
		 ** what will fit.
		 output数组剩余空间不足以 存放nchars个字符 
		 */
		if (output_col + nchars > MAX_INPUT - 1)
			nchars = MAX_INPUT - output_col - 1;

		/*
		 ** Copy the relevant data.
		 */
		strncpy(output + output_col, input + columns[col], nchars);
		output_col += nchars;
	}

	output[output_col] = '/0';
}

程序讲解:

        主要讲rearrange函数,在循环中的逻辑。这里的各种条件语句主要是为了处理 “复制范围” 和 “输入数组 以及 输出数组” 的关系。
首先,讲一下 范围 和 一个数组的长度 的三种关系:①范围在长度内;②范围部分在长度内;③范围在长度外;例如数组长度为10,那么范围[4,8]在长度内,[6, 9]部分在范围内;[11, 14]在范围外。
接着,再看 范围 和 两个数组长度 的对应关系:这时有9种情况。
    范围在数组1内,并且在数组2内 部分在数组2中 数组2外;
    范围部分在数组1中,并且在数组2内 部分在数组2中 在数组2外;
    范围在数组1外,并且在数组2内 部分在数组2中 在数组外。

        知道范围和两个数组长度的对应关系,就很容易分析这里的程序,在rearrange函数中的每一次循环中,存在一个范围,columns[col]到columns[col+1];两个数组,input和output数组。后面的各种判断,都是为了针对这个范围和两个数组的的9种关系,当然,根据题目的具体情况,这9种情况有部分情况不需要处理。

假定程序中input是是数组1,output是数组2,那么
1. 不需要处理的情况有: ①④;(对于情况④,隐含在函数strncpy函数中处理,strncpy和strcpy函数一样,在遇到字符串的结束字符'/n'时,会停止复制。 因为程序默认是范围是递增的顺序,那么出现情况④时,那么说明这就是最后一个循环,也不存在将'/0'提前复制的问题。(参见下面扩展3,那里需要考虑提前复制字符'/0'的问题)
2. if(columns[col] >= len):用来处理情况⑦⑧⑨;
3. if(output_col == MAX_INPUT - 1):用来处理情况③⑥;
4. if(output_col + nchars > MAX_INPUT - 1):用来处理情况②⑤;

程序扩展:

1.   在rearrange函数中,能够用strcpy函数来代替strncpy函数,即写成”strncpy(output + output_col, input + columns[col], nchars);”?
答:可以,但不推荐。你可能认为是不可以的,但有意思的是这里确实是可以的。由于strcpy会复制到字符串的结尾,即会比原来复制的字符要多。这里有意思的事情就发生了,如果你做试验的话,你会发现程序是能够正确运行的。原因在于,像output数组复制时,开始位置和output_col变量指定,无论使用的是strcpy函数还是strncpy函数,该变量都是正确更新的,(语句为:output_col += nchars;)。下次复制会覆盖前面复制的结果,在复制完成后也能够正确添加结束字符 '/0',所以程序能够正确执行。有一个隐患是:如果output数组不够大,那么有可能复制的数据超过output数组的容量,导致溢出,其实这就是程序中nchar的主要作用,用来防止复制的数据容量查过output的容量。但是这个在本例中是假定了input和output数组一样大,所以不会出现output的容量不够的现象。 

2.   程序中while(gets(input)!=NULL){……}的隐患是什么?
答:也是和数组溢出有关,gets函数无法防止一个非常长的输入。尽管这里假定input的长度是1000,但是当我们舒服一个超过1000个字符的行,就会引发错误。在文件操作时的fgets函数是没有这个问题的,因为它需要指定输入的长度。

3.  在rearrange函数中,
if (columns[col] >= len  ……)
            break;
该语句的意思是当字符的列范围(在运行时第一行输入指定的)超过输入行的末尾时就停止复制。这条语句如果想成功执行,就需要第一行所给的列范围是递增顺序输入的,如何修改程序,让程序允许我们不使用递增顺序。注:输入的时候是成对输入的,这里的变化是讲:后两个可以比前两个小,例如原来的递增顺序是 "4 9 12 20 -1" ,现在程序修改后要可以处理:"12 20 4 9 -1"。当然在成对的两个数中,后面的书还是要比前面的数大,比如:20要大于12,9要大于4,不能写为20 12 9 4 -1。
分析:这时候在rearrange函数中,如果遇到范围大于输入数组的长度的情况,即 columns[col] >= len,那么就不能退出循环,而是要跳过本次,因为有可能以为后面还有符合要求的范围。另外,对于输入数组,给出的范围可能是一部分在input数组中,即上面讨论一个范围和两个数组的关系时的情况④。

代码如下:

void rearrange(char *output, char const *input, int n_columns,
		int const columns[]) {
	int col; /* subscript for columns array */
	int output_col; /* output column counter */
                    /*注:output_col的值等于strlen(output),即output数组的长度*/
	int len; /* length of input line */
             /*注:len的值等于strlen(input),即input数组的长度,不包括最后的字符'/0',即这里最大为(MAX-INPU-1)*/
	len = strlen(input);
	output_col = 0;

	/*
	 ** Process each pair of column numbers.
	 */
	for (col = 0; col < n_columns; col += 2) {
		int nchars = columns[col + 1] - columns[col] + 1;
        /*给出的数据超过input的长度*/ 
        if(columns[col] >= len){
            continue;                
        } 
	    /*输出数组已满*/ 
		if ( output_col == MAX_INPUT - 1)
			break;
        
        /*为了防止过早的复制进,inout末尾的'/0'字符*/
        if(columns[col]+nchars-1 >= len){
            nchars = len-columns[col];
            printf("输入的长度为:%d /n", len); 
            printf("为了防止过早复制/0,这里做了调整"); 
        } 

	    /*
		 ** If there isn't room in the output array, only copy
		 ** what will fit.
		 */
		if (output_col + nchars > MAX_INPUT - 1){
            nchars = MAX_INPUT - output_col - 1;
		    printf("输出数组不够,这里做了调整"); 
        }
		/*
		 ** Copy the relevant data.
		 */
		strncpy(output + output_col, input + columns[col], nchars);
		output_col += nchars;
	}

	output[output_col] = '/0';
}

4.  在源程序中,第一行指定范围时,一定是要成对输入,即要同时指定开始和结束的位置。 那么怎么修改使程序可以处理奇数个输入?如果是奇数个,那么程序把最后一个列范围的结束设置为到输入行的结束。
分析:由于但输入的确定范围的数目是奇数时,是将最后一个范围扩展到input的结束位置,所以这里还是要求输入时按照递增顺序的。
这时首先要将read_column_numbers函数中的判断输入的个数是奇数还是偶数的代码删掉;然后要先进行判断,才能计算nchars。

代码如下:

删去read_column_numbers函数中的下列代码:

	if (num % 2 != 0) {
		puts("Last column number is not paired.");
		exit(EXIT_FAILURE);
	}

将rearrang函数修改为:

void rearrange(char *output, char const *input, int n_columns,
		int const columns[]) {
	int col; /* subscript for columns array */
	int output_col; /* output column counter */
                    /*注:output_col的值等于strlen(output),即output数组的长度*/
	int len; /* length of input line */
             /*注:len的值等于strlen(input),即input数组的长度*/
	len = strlen(input);
	output_col = 0;

	/*
	 ** Process each pair of column numbers.
	 */
	for (col = 0; col < n_columns; col += 2) {
        int nchars;
        
        if(col+1 < n_columns){
            nchars = columns[col + 1] - columns[col] + 1;      
        }else{
            nchars = len ;/*如果输入的为奇数,那么这里将input的长度赋值给nchars,后面strcpy函数复制时会将从此处开始到结尾*/
        }

		/*
		 ** If the input line isn't this long or the output
		 ** array is full, we're done.
		 columns中指定的范围大于inpput数组的长度, 或者output数组已满, 这时循环结束 
		 */
		if (columns[col] >= len || output_col == MAX_INPUT - 1)
			break;

		/*
		 ** If there isn't room in the output array, only copy
		 ** what will fit.
		 output数组剩余空间不足以 存放nchars个字符 
		 */
		if (output_col + nchars > MAX_INPUT - 1)
			nchars = MAX_INPUT - output_col - 1;

		/*
		 ** Copy the relevant data.
		 */
		strncpy(output + output_col, input + columns[col], nchars);
		output_col += nchars;
	}

	output[output_col] = '/0';
}

 

本文链接:http://blog.csdn.net/daheiantian/archive/2011/01/04/6151433.aspx

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值