C语言K&R圣经笔记 第3章控制流 3.1语句和块 3.2 if-else 3.3 else-if

第3章控制流

语言的控制流语句指定了计算执行的顺序。在前面的例子中,我们已经见过了大部分常见的控制流语句;这里我们会将它补全,并给出比之前更精确的说明。

3.1 语句和块

如 x = 0 或 i++ 或 printf(...) 这样的表达式,在末尾添加分号之后,就成为了语句,如

x = 0;
i++;
printf(...);

在C中,分号是语句的结束符,而不是分隔符(如Paslcal这样的语言中的分号是分隔符)。

大括号 { 和 } 用来将一些声明和语句组合成复合语句,或者称为,这样它们在语法上和单条语句是等价的。一个显著的例子是用来包围函数内所有语句的大括号;而另一个例子是 if,else,while 和 for 后面包围多条语句的大括号。(变量可以在任意块中声明,这个将在第四章讨论。)块末尾的右大括号后面,没有分号。

3.2 if-else

if-else 语句用来表示决策。正式的语法为

if (表达式)

        语句1

else

        语句2

其中 else 部分是可选的。对表达式求值;如果它为真(即表达式有非0值),则执行语句1。如果它为假(表达式为0)且存在 else 部分,则执行语句2。

既然 if 只会简单地检查表达式的数值,则某些代码就能有简化写法。最明显的是写

if (表达式)

而不写

if (表达式 != 0)

有时这个写法是自然而且清晰的,而有时会令人费解。

因为 if-else 的 else 部分是可选的,在嵌套的 if 序列内去掉 一个else 就会造成歧义。通过将 else 关联到最近一个没有 else 的 if,可以消解这个歧义。例如

if (n > 0)
    if (z > b)
        z = a;
    else
        z = b;

其中的 else 关联里面的 if,与我们的缩进一致。如果这不是你要的代码逻辑,就需要使用大括号来强制得到合适的关联:

if (n > 0) {
    if (z > b)
        z = a;
} else
    z = b;

这种歧义在一些情况下特别恶劣,比如:

if (n >= 0)
    for (i = 0; i < n; i++)
        if (s[i] > 0) {
            printf("...");
            return i;
        }
else     /*  错了!!! */
    printf("error -- n is negative\n");

缩进明确表示了你想要什么,但编译器无法获取这个信息,并将 else 关联到内层的 if 。这种类型的 bug 很难找;而只要遇到嵌套的 if,就加上大括号,是个不错的主意。

顺带一提【应该是给Pascal程序员看的】,下面的 z = a 后面有分号。

if (n > 0)
    if (z > b)
        z = a;
    else
        z = b;

这是因为在语法上,if 后面跟的是个语句,而表达式语句如 “ z = a; ” 总是以分号结尾。

3.3 else-if

if (expression)
    statement
else if (expression)
    statement
else if (expression)
    statement
else if (expression)
    statement
else
    statement

上面这个结构在C程序中出现得如此频繁,因此值得将它单独拿出来简要地讨论一下。它是写多路选择的最常用的方式。其中的表达式(expression)被依次求值;如果任一表达式为真,则其关联的语句(statement)被执行,且整个链条结束。当然,其中的每个“语句”可以是单条语句,或者是大括号中的一组语句。

当其他条件都不满足时,最后一个 last 部分用来处理“以上皆不是”或者说默认情况。有时对默认情况不需要做显式的动作,此时末尾的

else
    statement

就可以去掉,或者用来进行错误校验,捕获“不可能发生”的情况。

下面这个二分查找函数用来确定一个特定的值 x 是否存在于一个已排序的数组 v 中,它演示了三路决策。v 的元素必须是升序的。如果 x 存在于 v 中,函数返回其所在的位置(0 到 n - 1之间的值),否则返回 -1。

二分查找首先将输入值 x 与 数组 v 中间的元素进行比较。如果 x 小于中间的值,则搜索聚焦于数组的下半部分,否则聚焦于上半部分。不管是哪种情况,下一步是将 x 与 所选一半的中间值进行比较。这个过程持续地将范围一分为二,直到找到值,或者范围为空。

/* 二分查找:在 v[0] <= v[1] <= ... <= v[n-1] 中找到x */
int binsearch(int x, int v[], int n)
{
    int low, high, mid;

    low = 0;
    high = n - 1;

    while (low <= high) {
        mid = (low + high) / 2;
        if (x < v[mid])
            high = mid -1;
        else if (x > v[mid])
            low = mid + 1;
        else    /* 找到匹配值 */
            return mid;
    }
    return -1; /* 没找到 */
}

基本的决策是在每步要判断 x 是小于、大于或等于中间元素 v[mid] 的值;用 else-if 是很自然的。

练习 3-1、我们的二分查找在循环中做了两次判断,而一个判断就足够了(代价是在外面做更多判断)。写个函数只在循环内做一次判断,并测量运行时间的差异。

你可能感兴趣的:(C语言,笔记,c语言)