跳转至

语句

本文为《C 与 指针读书笔记》,感兴趣的读者可以去看原书。

C实现了其他现代高级语言所具有的所有语句。而且,它们中的绝大多数都是按照你所预期的方式工作的。if语句用于在几段备选代码中选择运行其中的一段,而while、for和do语句则用于实现不同类型的循环。

但是,和其他语言相比,C的语句还是存在一些不同之处。例如,C并不具备专门的赋值语句,而是统一用“表达式语句”代替。switch语句实现了其他语言中case语句的功能,但其实现的方式却非比寻常。

空语句

C最简单的语句就是空语句 ,它本身只包含一个分号。空语句本身并不执行任何任务,但有时还是有用。它所适用的场合就是语法要求出现一条完整的语句,但并不需要它执行任何任务。

表达式语句

既然C并不存在专门的“赋值语句”,那么它如何进行赋值呢?答案是赋值就是一种操作,就像加法和减法一样,所以赋值就在表达式内进行。

你只要在表达式后面加上一个分号,就可以把表达式转变为语句。所以,下面这两个表达式

x = y + 3;
ch = getchar();

实际上是表达式语句,而不是赋值语句。

代码块

代码块就是位于一对花括号之内的可选的声明和语句列表。

代码块可以用于要求出现语句的地方,它允许你在语法要求只出现一条语句的地方使用多条语句。代码块还允许你把数据的声明非常靠近它所使用的地方。

if语句

C的if语句和其他语言的if语句相差不大。

在C的if语句和其他语言的if语句中,只存在一个差别。C并不具备布尔类型,而是用整型来代替。这样,expression可以是任何能够产生整型结果的表达式——零值表示“假”,非零值表示“真”。

C拥有所有你期望的关系操作符,但它们的结果是整型值0或1,而不是布尔值“真”或“假”。关系操作符就是用这种方式来实现其他语言的关系操作符的功能。

while语句

C的while语句也和其他语言的while语句有许多相似之处。唯一真正存在差别的地方就是它的expression部分,和if语句类似。

break和continue语句

在while循环中可以使用break语句,用于永久终止循环。在执行完break语句之后,执行流下一条执行的语句就是循环正常结束后应该执行的那条语句。

在while循环中也可以使用continue语句,它用于永久终止当前的那次循环。在执行完continue语句之后,执行流接下来就是重新测试表达式的值,决定是否继续执行循环。

for语句

C的for语句比其他语言的for语句更为常用。事实上,C的for语句是while循环的一种极为常用的语句组合形式的简写法。

for循环有一个风格上的优势,它把所有用于操纵循环的表达式收集在一起,放在同一个地点,便于寻找。当循环体比较庞大时,这个优点更为突出。

do语句

C语言的do语句非常像其他语言的repeat语句。它很像while语句,只是它的测试在循环体执行之后才进行,而不是先于循环体执行。所以,这种循环的循环体至少执行一次。

我们如何在while语句和do语句之间进行选择呢?

当你需要循环体至少执行一次时,选择do。

switch语句

C的switch语句颇不寻常。它类似于其他语言的case语句,但在有一个方面存在重要的区别。首先让我们来看看它的语法,其中expression的结果必须是整型值。

其次需要注意的是,执行流将贯穿各个case标签,而不是停留在单个case标签,这也是为什么case标签只是确定语句列表的进入点而不是划分它们的原因。如果你觉得这个行为看上去不是那么正确,有一种方法可以纠正——就是break语句。

goto语句

要使用goto语句,你必须在你希望跳转的语句前面加上语句标签。语句标签就是标识符后面加个冒号。包含这些标签的goto语句可以出现在同一个函数中的任何位置。

goto是一种危险的语句,因为在学习C的过程中,很容易形成对它的依赖。经验欠缺的程序员有时使用goto语句来避免考虑程序的设计。这样写出来的程序较之细心编写的程序总是难以维护得多。

但是,在一种情况下,即使是结构良好的程序,使用goto语句也可能非常合适——就是跳出多层嵌套的循环。由于break语句只影响包围它的最内层循环,要想立即从深层嵌套的循环中退出只有使用一个办法,就是使用goto语句。

总结

C的许多语句的行为和其他语言中的类似语句相似。if语句根据条件执行语句,while语句重复执行一些语句。由于C并不具备布尔类型,所以这些语句在测试值时用的都是整型表达式。零值被解释为假,非零值被解释为真。for语句是while循环的一种常用组合形式的速记写法,它把控制循环的表达式收集起来放在一个地方,以便寻找。do语句与while语句类似,但前者能够保证循环体至少执行一次。最后,goto语句把程序的执行流从一条语句转移到另一条语句。在一般情况下,我们应该避免goto语句。

C还有一些语句,它们的行为与其他语言中的类似语句稍有不同。赋值操作是在表达式语句中执行的,而不是在专门的赋值语句中进行。switch语句完成的任务和其他语言的case语句差不多,但switch语句在执行时贯穿所有的case标签。要想避免这种行为,你必须在每个case的语句后面增加一条break语句。switch语句的default子句用于捕捉所有表达式的值与所有case标签的值均不匹配的情况。如果没有default子句,当表达式的值与所有case标签的值均不匹配时,整个switch语句体将被跳过不执行。

当需要出现一条语句但并不需要执行任何任务时,可以使用空语句。代码块允许你在语法要求只出现一条语句的地方书写多条语句。当循环内部执行break语句时,循环就会退出。当循环内部执行continue语句时,循环体的剩余部分便被跳过,立即开始下一次循环。在while和do循环中,下一次循环开始的位置是表达式测试部分。但在for循环中,下一次循环开始的位置是调整部分。

就是这些了!C并不具备任何输入/输出语句;I/O是通过调用库函数实现的。C也不具备任何异常处理语句,它们也是通过调用库函数来完成的。

编程练习

练习1

一个整数如果只能被它本身和1整除,它就被称为质数(prime)。请编写一个程序,打印出1~100之间的所有质数。

#include <stdio.h>
#include <stdbool.h>

int main() {
    int num, i;
    bool isPrime;

    // 遍历1到100之间的所有数字
    for (num = 2; num <= 100; num++) {
        isPrime = true; // 假设当前数字是质数

        // 检查num是否是质数
        for (i = 2; i <= num / 2; i++) {
            if (num % i == 0) {
                isPrime = false; // 如果找到其他除数,则num不是质数
                break; // 退出当前循环
            }
        }

        // 如果num是质数,则打印出来
        if (isPrime) {
            printf("%d\n", num);
        }
    }

    return 0;
}

练习2

等边三角形的三条边长度都相等,但等腰三角形只有两条边的长度是相等的。如果三角形的三条边长度都不等,那就称为不等边三角形。请编写一个程序,提示用户输入三个数,分别表示三角形三条边的长度,然后由程序判断它是什么类型的三角形。提示 :除了边的长度是否相等之外,程序是否还应考虑一些其他的东西?

#include <stdio.h>

int main() {
    int a, b, c; // 分别代表三角形的三条边

    printf("请输入三角形三条边的长度(用空格分隔): ");
    scanf("%d %d %d", &a, &b, &c);

    // 检查是否能构成三角形
    if (a + b > c && a + c > b && b + c > a) {
        // 如果可以构成三角形,接着判断三角形的类型

        if (a == b && b == c) {
            // 三边相等
            printf("这是一个等边三角形。\n");
        } else if (a == b || a == c || b == c) {
            // 两边相等
            printf("这是一个等腰三角形。\n");
        } else {
            // 无边相等
            printf("这是一个不等边三角形。\n");
        }
    } else {
        // 不能构成三角形
        printf("这三条边无法构成三角形。\n");
    }

    return 0;
}

练习 3

编写一个函数,从一个字符串中去除多余的空格。当函数发现字符串中如果有一个地方由一个或多个连续的空格组成,就把它们改成单个空格字符。注意当你遍历整个字符串时要确保它以NUL字符结尾。

#include <stdio.h>

void deblank(char string[]) {
    int readPos = 0, writePos = 0; // 分别表示读取和写入的位置指针
    int spaceFound = 0; // 用于标记是否找到空格

    // 遍历字符串直到遇到 NUL 字符终止
    while (string[readPos]) {
        if (string[readPos] == ' ') {
            // 如果找到空格且之前没有找到空格,则写入一个空格到当前写入位置
            if (!spaceFound) {
                string[writePos++] = string[readPos];
                spaceFound = 1; // 标记找到了空格
            }
        } else {
            // 如果找到的不是空格,则直接将字符写入当前写入位置
            string[writePos++] = string[readPos];
            spaceFound = 0; // 重置空格标记
        }
        readPos++; // 移动读取位置
    }

    string[writePos] = '\0'; // 在最后加上 NUL 字符结束字符串
}

int main() {
    char testStr[] = "This  is   a  test  string.";

    printf("Original string: '%s'\n", testStr);
    deblank(testStr);
    printf("Modified string: '%s'\n", testStr);

    return 0;
}

最后更新: January 5, 2025