浅谈类C语言中的for语句的用法

类别:编程语言 点击:0 评论:0 推荐:
首先,这里所提到的类C语言指的是如C、C++、C#和Java等语法和C语言一样或类似的程序设计语言。这些语言中,for语句的语法和执行流程都是一样的。本文将就这一语句的用法进行一个较为深入的讨论。
    熟悉BASIC语言的用户都知道,在BASIC中FOR循环是通过指定初值、终值和步长来确定一个循环的。例如,要对一个长度为10的数组进行初始化,我们应当写作:
FOR i = 0 TO 9 STEP 1
    a(i) = i * i
NEXT i
    其中,步长以及NEXT语句中所指定的变量可以省略。因此,上面的循环甚至可以相当简单地写作:
FOR i = 1 TO 10
    a(i) = i * i
NEXT
    我们可以看出,这段语句的可读性是相当高的,只需要扫视一下,我们就知道这个循环将执行10次,并将一个长度为10的数组的每个元素初始化为其下标的平方值。
    可如果在类C语言中,我们要这样写:
for(i = 0; i <= 9; i++) {
    a[i] = i * i;
}
    对于熟悉类C语言的读者,这段代码可能已经习以为常了;但对于一个初学者,总会觉得有点难以捉摸,尤其是很难对循环终止的时刻作出判断。譬如,比较下面两个循环:
/* 循环1: */
for(i = 0; i <= 9; i++) {
    a[i] = i * i;
}

/* 循环2: */
for(i = 0; i < 9; i++) {
    a[i] = i * i;
}
    熟悉类C语言的读者很清楚,循环1将执行10次而循环2只会执行9次;而循环2恰恰又是大多数类C语言程序员所常用的形式。
    那么,大家可能为有疑问:类C语言都是通过其语法的简洁性和执行的高效性而深受大家欢迎的,为什么在for循环上却表现得如此复杂呢?
    我们知道,C语言在追求简洁和高效的同时,还在灵活性上达到了一个很高的目标。for语言的这种设计,正是高度的灵活性的体现。
    首先,我们说说类C语言的for语句的执行流程。
    类C语言的for语句通常具有如下形式:
for(statement1; statement2; statement3) {
    /* body */
}
    这里,statement1、statement2和statement3是一般的语句。其中statement2应具有布尔类型,但在C中具有int类型(因为C语言不支持布尔类型)。这段语句的执行流程如下:
第1步:对statement1求值。
第2步:判断statement2是否为真(在C中判断是否不为0),如果不为真(在C中是为0)则执行结束;否则继续
第3步:执行循环体。
第4步:执行statement3,转到第2步。
    在不了解for语句的执行流程之前,有些初学者(包括我过去)可能会这样认为:类C语言for语句中的statement1(通常是一个赋值语句)相当于BASIC语言FOR语句中的初值设置,类C语言for语句中的statement2语句(通常是一个判断大小的语句)相当于BASIC语言FOR语句中的终值判断,而类C语言中的statement3语句(通常是一个对循环变量进行自加的语句)相当于BASIC语言FOR语句中的步长设置。
    但是,在了解了这一流程之后,我们就应当灵活地运用类C语言的for语句了。对于一个for循环,我们可以首先通过statement1对循环过程作一个初始化(通过类C语言中的逗号表达式,甚至可以在这里执行多个语句),然后在statement2对循环的终止作出判断,而在每次循环体执行完毕后通过statement3改变循环过程的状态。
    举个实际的例子,我在学习数据结构时用C语言开发了自己的函数库,其中对于链表的插入、删除以及遍历等操作中查找插入点(删除点)位置的过程中,我大量地运用了for语句的灵活用法。这里以遍历为例:
typedef void (*walk_func_t)(void *);

void WalkOn(SLList *list, walk_func_t walk) {
    SLListNode *p;

    for (
        p = list->head;  /* 设置初始状态    */
        p != NULL;       /* 判断循环何时结束 */
        p = p->next      /* 进入下一状态    */
    ) {
        walk(p->data);   /* 遍历当前节点    */
    }
}
    这里,我不想对链表的具体细节作出讨论,仅考虑for循环,其执行过程如下:
第一步:设置p指向链表的第一个节点。
第二步:判断p是否为空(到达链表结尾),如果为空则退出循环;否则继续。
第三步:用传递进来的函数指针在当前节点(p所指向的节点)所指向的数据对象上执行某些操作。
第四步:令p指向下一个节点;转到第二步。
    通过这样一个过程,我们可以很清晰地看出遍历动作的执行流程。
    很多人看到这段代码可能会相当郁闷,他们通常(甚至是一定的)这样写这个函数:
void WaklOn(SLList *list, walk_func_t walk) {
    SLListNode *p = list->head;

    while(p != NULL) {
        walk(p->data);
        p = p->next;
    }
}
    大家可能认为这样的代码更可读。但我觉得这是因为大家对类C语言的各种语句的执行不甚了解所致。由于没有大师提到过我的用法,因此很多人难以接受。但是,当大家对for语句的执行流程有所了解后,我所提到的方法实际上具有更高的可读性。在上面我所写出的代码中,一条格式良好的for语句清晰地展示了遍历动作的过程(注释完全可以去掉); 而要想看懂while循环所实现的遍历函数,则要求读代码的人对链表及其遍历动作有所了解才行。
    最后,本文通过对比BASIC和类C语言,展示了类C语言语法上的灵活性。然后我介绍了我自己发明的一种for语句的用法,充分利用了这种灵活性,大地提高了程序的可读性。不管你是否喜欢我的方法、是否能看懂它,我希望大家都应该加强对程序设计语言本身的学习,这是编写实用程序的基础。我们应当充分利用语言的灵活性,设计可读性更高的程序。

本文地址:http://com.8s8s.com/it/it25546.htm