1、 C语言格式自由,但编写程序时遵守一些约定会使代码更加容易阅读和修改,清晰显示程序很重要,但让读者知道程序是做什么的更加重要,注释就是实现这个功能,格式优美,让人舒服。
2、 注释掉一段代码,即使这段代码在程序中不起作用,但并没有将其真正从源文件中删除,要从逻辑上删除一段代码更好的办法是使用#if指令,即:
#if 0
statements
#endif
在 #if和#endif之间的程序段可以有效的从程序中去除,即使这段代码之间原先存在注释也无妨,预处理指令的作用很大。
3、函数原型:每个原型以一个类型名开头,表示函数返回值的类型,跟在返回类型名后面的是函数的名字,然后跟着函数期望接受的参数。
如:void rearrange(char *output,char const *input,int n_columns,int const columns[]);//rearrange函数接受四个参数,关键字void表示函数并不返回任何值.
4、在C语言中,数组参数是以引用形式传递的,也就是传址调用,而标量和常量则是按值传递。被调用函数无法修改调用函数以传值形式传递给它的参数,然而,当被调用函数修改数组参数的其中一个元素时,调用函数所传递的数组就会被实际的修改,如下,给出关键部分:
.........
#define MAC_COLS 20
.......
int main()
{
....
int columns[MAX_COLS];
....
n_columns = read_column_numbers(columns,MAX_COLS);//调用函数read_column_numbers,数组columns和MAX_COLS //所代表的常量20作为参数传 递给这个函数
....
}
.......
5、和绝大多数语言一样,C语言中形式参数的名字和实际参数的名字并没有什么关系,可以让两者相同,也可不同。
6、while(num < max && scanf("%d",&columns[num]) == 1 && columns[num] >= 0)
此while语句中循环的测试条件由三部分组成:num<max为了确保函数不会读取过多的值,从而导致数组溢出;scanf("%d",&columns[num]) == 1,scanf函数每次调用时都从标准输入读取一个十进制整数,如果转换失败,函数返回0,整个循环终止,若输入的字符可以合法的转换为整数,那么这个值就会转换为二进制数存储于数组元素columns[num]中,scanf函数返回1表示成功; columns[num] >= 0,这个表达式确保函数所读取的值是正数。整个语句中第二个&&操作符确保在scanf函数成功读取了一个数后才对这个数进行正负测试。&&:逻辑与,&:按位与。
Note:scanf函数实现的原理:所有标量参数的前面必须加上一个"&"符号,数组前面不需要加上"&"符号,但是数组参数中如果出现了下标引用,即实际参数是某个特定元素,前面必须加"&".
7、while ((ch = getchar()) != EOF && ch != ‘\n‘);
等价于:
ch = getchar();
while (ch != EOF && ch != ‘\n‘);
ch = getchar();
getchar函数从标准输入读取一个字符并返回它的值,如果输入中不再存在任何字符,函数就会返回EOF(在stdio.h中定义),用于提示文件的结尾。从getchar函数返回的值被赋给变量ch,然后把它与EOF进行比较,若ch = EOF,为假,循环终止,若非如此,再把ch与换行符比较,若相等,则循环终止。即只有当输入尚未到达文件尾且输入的字符并非换行符时,表达式为真,继续读取下一个字符,达到剔除当前输入行最后的剩余字符(终止符的换行符或其他字符)。C可以把复制操作蕴含于while语句内部,可以消除冗余语句。
8、get函数从标准输入读取一行文本并把它存储于作为参数传递给它的数组中,一行输入由一串字符组成,以一个换行符(newLine)结尾。gets函数丢弃换行符,并在该行的末尾存储一个人NUL字节(一个NUL字节是指字节为全0的字节,类似‘\0‘这样的字符常量)。gets函数返回一个非NULL值,表示该行已被成功读取,当gets函数被调用但事实上不存在输入行时,它就返回NULL值,表示它达到了输入的末尾(文件尾)。
约定:字符串就是就是一串以NUL字节结尾的字符,NUL是作为字符串终止符,它本身不被看作是字符串的一部分。字符串常量(string literal)就是源程序中被双引号括起来的一串字符,例如,字符串常量:"Hello",在内存中占据6个字节的空间,按顺序分别是H、e、l、l、o和NUL.
note:NUL是ASCII字符集中‘\0‘字符的名字,它的字节模式为全0,NULL指一个其值为0的指针,他们都是整型值,其值相同,可以互换使用。
.....
int main()
{
.....
while(gets(input) != NULL)
{
printf("Original input:%s\n",input);
rearrange(output,input,n_columns,columns);//rearrange函数的三个参数是传递给函数的值
printf("Rearranged line:%s\n",output);//显示输入行重新整理后的结果
}
return EXIT_SUCCESS;//表示程序成功执行
}
.....
9、strncpy函数把选中的字符从输入行复制到输出行中可用的下一个位置。strncpy函数的前两个参数分别是目标字符串和源字符串的地址,在这个调用中,目标字符串的位置是输出数组的起始地址向后偏移output_col列的地址,源字符串的位置是输出数组的起始地址向后偏移columns[col]个位置的地址,第3个参数指定需要复制的字符数,输出列计数器随后向后移动nchars个位置。循环结束后,输出字符以一个NUL字符作为终止符。
...
int main()
{
...
}
...
void rearrange(char *output,char const *input,int n_columns,int const columns[])
{
...
strncpy(output + output_col,input + columns[col],nchars);
output_col += nchars;
output[output_col] = ‘\0‘;
...
}
补充说明:
strcpy:与strncpy相似,但没有限制需要复制的字符数量,它接受两个参数:第二个字符串参数将被复制到第一个字符串参数,第一个字符串参数将被覆盖。
stract:接受两个参数,把第二个字符串参数添加到第一个字符串参数的末尾。
注:第一个字符串参数不能是字符串常量,确保目标字符串有足够的空间。
strchr:第一个参数字符串,第二个参数一个字符,实现搜索功能:在字符串参数内搜索字符参数第1次出现的位置,成功返回指向这个位置的指针,失败返回一个NULL指针。
strstr:与strstr类似,区别第二个参数是字符串,搜索第二个字符串在第一个字符串中第1次出现的位置。
完整代码:
/*
**这个程序从标准输入中读取输入行并在标准输出中打印这些输入行
**每个输入行的后面一行是该行内容的一部分
**
**输入的第一行是一串列标号,串的最后以一个负数结尾
**这些列标号成对出现,说明需要打印的输入行的列范围
**例如:0 3 10 12 -1表示第 0 列到第 3 列,第 10 列到第 12 列的内容将被打印
*/
//预处理器指令
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_COLS 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[]);
int main()
{
int n_columns;//进行处理的列标号
int columns[MAX_COLS]; //需要处理的列数
char input[MAX_INPUT];//容纳输入行的数组
char output[MAX_INPUT];//容纳输出行的数组
/*
**读取该串列标号
*/
n_columns = read_column_numbers(columns,MAX_COLS);
/*
** rearrange函数:处理输入行,将指定列的字符连接在一起,输出行以NUL结尾
** while 当还可以读取另一行输入时
** 打印输入行
** 对输入行进行重新整理,把它存储于output数组
** 打印输出结果
*/
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;
/*
**取得列标号,如果所读取的数小于0则停止
*/
while(num < max && scanf("%d",&columns[num]) == 1 && columns[num] >= 0)
num += 1;
/*
**确认已经读取的标号为偶数个,因为它们是以对的形式出现的
*/
if(num % 2 != 0)
{
puts("Last column number is not paired.");
exit(EXIT_FAILURE);
}
/*
**丢弃该行中包含最后一个数字的那部分内容
*/
while ((ch = getchar()) != EOF && ch != ‘\n‘);
return num;
}
/*
**处理输入行,将指定列的字符连接在一起,输出行以 NUL 结尾
*/
void rearrange(char *output,char const *input,int n_columns,int const columns[])
{
int col; //columns数组的下标
int output_col; //输出列计数器
int len; // 输入行的长度
len = strlen(input);
output_col = 0;
/*
**处理每对列标号
*/
for(col = 0;col < n_columns;col += 2)
{
int nchars = columns[col + 1] - columns[col] + 1;//需要复制的字符数
/*
**如果输入行结束或输出行数组已满,就结束任务
*/
if(columns[col] >= len || output_col == MAX_INPUT - 1)
break;
/*
**如果输出行数组空间不够,只复制可以容纳的数据
*/
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‘;
}