阐述 C 语言中的短路求值?

C语言

🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!
📙C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂,深入浅出,匠心打磨,死磕细节,6年迭代,看过的人都说好。

分割线

分割线


C 语言中的短路求值

在 C 语言中,短路求值(Short-circuit Evaluation)是一种逻辑表达式求值的优化策略。当对逻辑表达式进行求值时,如果根据表达式中已计算的部分就能够确定整个表达式的结果,那么剩余的部分将不再计算。这种特性在 C 语言的逻辑运算中经常被利用,并且在某些情况下对于提高程序的效率和避免不必要的计算非常有用。

一、逻辑运算符

C 语言中有三个逻辑运算符:&&(逻辑与)、||(逻辑或)和 !(逻辑非)。

&& 运算符要求两个操作数都为真时,整个表达式才为真;|| 运算符只要两个操作数中有一个为真,整个表达式就为真。

二、短路求值的原理

1. 逻辑与(&&)的短路求值

当使用 && 运算符时,如果左边的操作数为假,那么右边的操作数就不会被计算,因为无论右边的操作数的值如何,整个表达式都已经确定为假。

例如,考虑以下表达式:

int a = 0;
int b = 5;

if (a && b++) {
    // 不会执行到这里,因为 a 为 0,短路求值导致 b++ 不会被计算
}

在上述代码中,由于 a 的值为 0,所以整个 a && b++ 的表达式已经确定为假,因此 b++ 不会被计算,b 的值仍然为 5

2. 逻辑或(||)的短路求值

当使用 || 运算符时,如果左边的操作数为真,那么右边的操作数就不会被计算,因为无论右边的操作数的值如何,整个表达式都已经确定为真。

例如:

int a = 5;
int b = 0;

if (a || b++) {
    // 会执行到这里,因为 a 为 5,短路求值导致 b++ 不会被计算
}

在这个例子中,因为 a 的值为 5(即为真),所以 b++ 不会被计算,b 的值保持为 0

三、短路求值的优点

1. 提高程序效率

通过避免不必要的计算,可以节省 CPU 时间和资源。特别是在复杂的逻辑表达式中,或者当右侧操作数的计算成本较高时,短路求值的优势更加明显。

例如,如果右侧操作数涉及到一个复杂的函数调用或者耗时的计算,而左侧操作数已经能够确定整个表达式的结果,那么短路求值可以避免执行这些耗时的操作。

int expensiveComputation() {
    // 一个耗时且复杂的计算
    printf("Expensive computation is being performed!\n");
    return 1;
}

int main() {
    int a = 0;

    if (a || expensiveComputation()) {
        // 由于 a 为 0,expensiveComputation() 不会被调用
        printf("This line will not be printed.\n");
    }

    return 0;
}

在上述代码中,由于 a 的值为 0,在逻辑或 || 的短路求值作用下,expensiveComputation() 函数不会被调用,从而节省了计算资源和时间。

2. 防止错误和异常

在某些情况下,右侧操作数的计算可能会导致错误或异常,如果左侧操作数能够通过短路求值避免这种计算,就可以防止程序出现错误。

例如,如果右侧操作数涉及到对一个未初始化的指针进行解引用,或者对一个超出范围的数组进行访问,短路求值可以避免这些危险的操作。

int* ptr = NULL;

if (ptr!= NULL && *ptr == 5) {
    // 由于 ptr 为 NULL,*ptr == 5 不会被计算,避免了空指针解引用错误
    printf("This line will not be printed.\n");
}

在这个例子中,如果没有短路求值,对 *ptr 的访问将导致运行时错误。但由于短路求值,当 ptrNULL 时,*ptr == 5 不会被计算,从而避免了错误。

3. 使代码更简洁和易于理解

短路求值可以使某些逻辑表达更加简洁和直观。通过合理利用短路求值,可以避免一些复杂的条件判断和嵌套的 if 语句,使代码更易于阅读和维护。

例如,下面的代码使用短路求值来简洁地表达一个条件:

int hasData = 1;
int isValid = 0;

if (hasData && (isValid = validateData()) ) {
    // 只有当 hasData 为真且 validateData() 返回真时,才会执行这里
}

在上述代码中,通过短路求值,只有在 hasData 为真的情况下,才会调用 validateData() 函数并将其返回值赋给 isValid 来进一步判断条件。

四、短路求值的注意事项

1. 副作用

在短路求值中,如果右侧操作数包含有副作用(如变量的修改),并且由于短路求值而未被计算,可能会导致意外的结果。

int a = 0;
int b = 0;

if (a && ++b) {
    // 由于 a 为 0,++b 不会被计算
}

printf("b = %d\n", b); 
// 输出:b = 0,因为 ++b 由于短路求值未执行

在这个例子中,原本期望 ++b 会执行,但由于短路求值,b 的值没有被修改。

2. 依赖顺序的计算

如果逻辑表达式中右侧操作数的计算结果依赖于左侧操作数的计算结果,或者右侧操作数的计算会影响左侧操作数的值,那么短路求值可能会导致不正确的结果。

int a = 5;
int b = a++;

if (a++ && b++) {
    // 由于 a++ 的值为 6(真),b++ 不会被计算
}

printf("a = %d, b = %d\n", a, b); 
// 输出:a = 8, b = 5,因为 b++ 由于短路求值未执行

在上述代码中,b 的值应该在每次使用时都根据 a 的最新值进行计算,但由于短路求值,b++ 没有被执行,导致 b 的值没有按照预期更新。

3. 可读性

虽然短路求值可以使代码更简洁,但过度使用可能会降低代码的可读性。在复杂的逻辑中,确保代码的逻辑清晰易懂是很重要的。

int cond1 = 0;
int cond2 = 1;
int value = 5;

if ((cond1 && (value = 10)) || cond2) {
    // 复杂的逻辑,难以直观理解短路求值的效果
}

在这个例子中,逻辑表达式的结构比较复杂,使用短路求值可能会使其他开发者在阅读代码时难以快速理解其意图。

五、示例

以下是一些更复杂的示例,展示了短路求值在不同情况下的应用和影响。

示例 1:函数调用与短路求值

#include <stdio.h>

int checkCondition1() {
    printf("Checking condition 1...\n");
    return 0;
}

int checkCondition2() {
    printf("Checking condition 2...\n");
    return 1;
}

int main() {
    if (checkCondition1() && checkCondition2()) {
        printf("Both conditions are true.\n");
    } else {
        printf("At least one condition is false.\n");
    }

    return 0;
}

在上述代码中,当计算 checkCondition1() && checkCondition2() 时,由于 checkCondition1() 返回 0(假),根据短路求值,checkCondition2() 不会被调用。因此,输出将是:

Checking condition 1...
At least one condition is false.

示例 2:复杂条件与短路求值

#include <stdio.h>

int main() {
    int a = 0, b = 1, c = 2;

    if ((a == 0 && b++ == 1 && c++ == 2) || (a++ == 1 && b == 1 && c == 2)) {
        printf("The condition is true.\n");
    } else {
        printf("The condition is false.\n");
    }

    printf("a = %d, b = %d, c = %d\n", a, b, c);

    return 0;
}

在这个示例中,对于逻辑或 || 左边的表达式 (a == 0 && b++ == 1 && c++ == 2) ,由于 a == 0 为真,所以继续计算 b++ == 1 ,因为其也为真,所以继续计算 c++ == 2 ,由于其为真,所以整个左边的表达式为真。由于左边的表达式已经为真,根据短路求值,逻辑或右边的表达式 (a++ == 1 && b == 1 && c == 2) 不会被计算。

输出将是:

The condition is true.
a = 0, b = 2, c = 3

示例 3:指针与短路求值

#include <stdio.h>

int main() {
    int* ptr1 = NULL;
    int* ptr2 = NULL;

    if (ptr1!= NULL && *ptr1 == 5) {
        printf("Condition 1 is true.\n");
    } else {
        printf("Condition 1 is false.\n");
    }

    if (ptr2!= NULL || *ptr2 == 5) {
        printf("Condition 2 is true.\n");
    } else {
        printf("Condition 2 is false.\n");
    }

    return 0;
}

在第一个 if 语句中,由于 ptr1NULL ,根据短路求值,*ptr1 == 5 不会被计算,输出将是:

Condition 1 is false.

在第二个 if 语句中,由于 ptr2NULL ,但因为是逻辑或 || ,所以 *ptr2 == 5 不会被计算,输出将是:

Condition 2 is false.

六、总结

短路求值是 C 语言中逻辑表达式求值的一个重要特性,它可以提高程序的效率、避免错误,并使代码更加简洁和直观。然而,在使用短路求值时,需要注意副作用、依赖顺序的计算和可读性等问题,以确保程序的正确性和可维护性。通过合理地利用短路求值,并对其潜在的影响有清晰的理解,可以编写出更加高效和可靠的 C 语言程序。


分割线

🎉相关推荐

分割线



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值