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

相关推荐
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
美式小田1 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
猫爪笔记1 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
_不会dp不改名_1 小时前
HCIA笔记3--TCP-UDP-交换机工作原理
笔记·tcp/ip·udp
陌小呆^O^1 小时前
Cmakelist.txt之win-c-udp-server
c语言·开发语言·udp
时光の尘2 小时前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
-一杯为品-2 小时前
【51单片机】程序实验5&6.独立按键-矩阵按键
c语言·笔记·学习·51单片机·硬件工程
爱摸鱼的孔乙己3 小时前
【数据结构】链表(leetcode)
c语言·数据结构·c++·链表·csdn
Dola_Pan3 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法
IU宝3 小时前
C/C++内存管理
java·c语言·c++