《C和指针》—第一章:快速上手

10 篇文章 0 订阅

首先分析一个程序,这个程序从标准输入读取文本并且对其修改,然后把它写到标准输出。这个程序的重要之处在于向你展示了当你编程时所需要知道 的绝大多数基本技巧。

这个函数的功能是:

首先读取一系列包换,这些标号成对出现,表示输入行的列范围。这串标号以一个负值结尾,作为结束标志。然后打印成对出现的数字之间的字符。

例如:2     5      7      12      -1

            ssdafsdgfbgdfgadfgdfggfhjgjghkjghkfdga;

意味着你要打印第二列至第五列,第七列至第12列之间的字符。

例1.1

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_COLS 20
#define MAX_INPUT 100

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

int main(void) {
    int n_columns;
    int columns[MAX_COLS];
    char input[MAX_INPUT];
    char output[MAX_INPUT];

    n_columns = read_column_numbers(columns, MAX_COLS);

    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)
        num += 1;
    if(num % 2 != 0){
        puts("last column number is not paried.");
        exit(EXIT_FAILURE);
    }

    while((ch = getchar()) != EOF && ch != '\n')
        ;
    return num;
}

void rearrange(char *output, char const *input, int n_columns, int const columns[])
{
    int col;
    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';
}

然后我们对上边的程序进行详细的解释。 

空白和注释

空行将程序的不同部分分隔开来;制表符(tab)用于缩进语句,更好的显示结构。

注释不能嵌套。

注释有时候用于将一段代码注释掉,也就是使这段代码在程序中不起作用,但是并不能将其真正从源文件中删除。

在C语言中,如果你试图在一段代码的首尾分别加上注释符来注释掉这段代码,你不一定能够如愿。如果这段代码内部原先就有注释存在,这样做就会出问题。要从逻辑上删除一段代码,更好的办法就是使用#if指令。即:

#if 0
    statements;
#endif

这是一种更为安全的方法。

预处理指令

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_COLS 20
#define MAX_INPUT 100

这几行被称为预处理指令,因为他们是由预处理器解释的。

在我们的例子程序中,预处理器用明教stdlio.h的库函数头文件的内容替换第一条#include指令。

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

这些声明被称为函数原型。他们告诉编译器这些以后将在源文件中定义的函数的特征。这样,当函数被调用的时候,编译器就能对她们进行准确性检查。

声明中首先出现的是函数的返回值类型,第二个函数表示不返回任何值,接受四个参数,第一个和第二个接受的是指针,第2个,第4个参数声明为const,这表示函数将不会修改函数调用者所传递的这两个参数。

main()函数

每一个C程序都必须有一个main函数,这是程序执行的起点。

int n_columns;
int columns[MAX_COLS];
char input[MAX_INPUT];
char output[MAX_INPUT];

这几行声明了4个变量,一个整型变量,一个整形数组,两个字符数组。这是main函数的局部变量,其他函数不能调用。

n_columns = read_column_numbers(columns, MAX_COLS);

这条语句调用函数,第一个数组参数是以引用的形式传递的,也就是传址调用,而第二个参数标量和常量是以值传递的。在函数中对标量参数的任何修改都会在函数返回时丢失,因此,被调用函数无法修改调用调用函数以传值的形式传递给它的参数。

然后,当被调用函数修改数组参数的其中一个元素时,调用函数所传递的数组就会被实际的修改。

 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;

这个循环代表了这个程序的主要逻辑。简而言之:

while 我们还可以读取一行输入时;

          打印输入行

          对输出结果进行重新整理,把它存储于output之中;

          打印输出结果;

在C语言中,处理字符是常见的任务之一。尽管C语言并不存在“string”类型,但是在整个语言中,存在一项约定:字符串就是一串以NUL字节结尾的字符。NUL是作为字符串终止符,它本身并不被看作是字符串的一部分。字符串常量就是源程序中被双引号括起来的一串字符。

read_column_numbers函数

int read_column_numbers(int columns[], int max) {

函数定义与之前的声明完全匹配,如果不匹配就会报错。

这个函数的数组参数中,并未指定数组大小,它允许单个函数操纵任意长度的一维数组。这个特性的不利一面是函数无法知道数组额大小,如果确实需要知道数组的大小,则需要将数组大小作为一个单独的参数传递给函数。

while(num < max && scanf("%d", &columns[num]) == 1 && columns[num] >= 0)
        num += 1;

这是一个循环用于读取列标号。scanf函数的返回值是函数成功转换并存储于参数中的值的个数。

对于scanf这个函数,所有的标量前面必须加一个&符号,数组参数前面不需要加&符号,但是数组参数中如果出现了下标引用,也就是说实际参数是数组的某一个特定元素,那么他的前面也必须加上&符号。

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

这个测试检查程序所读取的整数是否为偶数个。

puts函数式gets函数的输出版本,他把指定的字符串写到标准输出并在末尾添加一个换行符;

程序接着调用exit函数终止程序的运行。

while((ch = getchar()) != EOF && ch != '\n')
        ;

当scanf函数对输入值进行转换时,它只读取需要读取的字符。getchar()函数从标准输入读取一个字符并返回它的值。如果输入中不再存在任何字符,函数就会返回常量EOF,用于提示文件的结尾。

只有当输入尚未到达文件尾并且输入的字符并非换行符时,表达式的值才是真的。

C可以把赋值语句操作蕴含到while语句内部,这样就允许程序员消除冗余语句。

在这里,ch需要读取字符,但是ch为什么被声明为整型??答案是EOF是一个整型值,他的位数比字符类型要多,把ch声明为整型可以防止从输入读取的字符以外的被解释成EOF。但同时,这也意味着接受字符的ch必须足够大,足以容纳EOF,这就是ch使用整型值的原因。

rearrange函数

void rearrange(char *output, char const *input, int n_columns, int const columns[])
{
    int col;
    int output_col;
    int len;

由于他的传址调用语义,如果函数修改了形参数组的元素,他实际上将修改实参数组的对应元素。因此把columns声明为const就有两方面的作用。首先,他声明该函数的作者意图是这个参数不能被修改。其次,它导致编译器去验证是否违背该意图。
 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值