C&C++快速入门

9、逻辑运算符

1、课程目标

  • 掌握逻辑与、逻辑或、逻辑非运算符
  • 理解短路求值的工作原理
  • 掌握关系运算符
  • 学会构建复杂的条件表达式

2、名词解释

术语 符号 说明
逻辑与 && 两个条件都为真则为真
逻辑或 || 任一条件为真则为真
逻辑非 ! 取反
短路求值 Short-circuit 能确定结果时不继续计算
等于 == 判断相等
不等于 != 判断不相等
大于 > 判断大于
小于 < 判断小于
大于等于 >= 判断大于或等于
小于等于 <= 判断小于或等于

3、代码实现

1. 关系运算符

#include <stdio.h>

int main() {
    printf("=== 关系运算符 ===\n\n");
    
    int a = 10, b = 20;
    
    printf("a = %d, b = %d\n\n", a, b);
    printf("a == b : %d\n", a == b);  // 0 (false)
    printf("a != b : %d\n", a != b);  // 1 (true)
    printf("a > b  : %d\n", a > b);   // 0
    printf("a < b  : %d\n", a < b);   // 1
    printf("a >= b : %d\n", a >= b);  // 0
    printf("a <= b : %d\n", a <= b);  // 1
    
    // 注意:比较结果是整数 0 或 1
    printf("\n比较结果类型: %d字节\n", (int)sizeof(a < b));
    
    return 0;
}

2. 逻辑运算符

#include <stdio.h>

int main() {
    printf("=== 逻辑运算符 ===\n\n");
    
    int a = 1, b = 0;
    
    printf("a = %d (真), b = %d (假)\n\n", a, b);
    
    // 逻辑与
    printf("【逻辑与 &&】\n");
    printf("a && b : %d\n", a && b);   // 0
    printf("a && a : %d\n", a && a);   // 1
    printf("b && b : %d\n", b && b);   // 0
    
    // 逻辑或
    printf("\n【逻辑或 ||】\n");
    printf("a || b : %d\n", a || b);   // 1
    printf("a || a : %d\n", a || a);   // 1
    printf("b || b : %d\n", b || b);   // 0
    
    // 逻辑非
    printf("\n【逻辑非 !】\n");
    printf("!a : %d\n", !a);           // 0
    printf("!b : %d\n", !b);           // 1
    printf("!!a : %d\n", !!a);         // 1 (双重取反还原)
    
    // 非零值都为真
    printf("\n【非零值为真】\n");
    printf("!0 : %d\n", !0);           // 1
    printf("!1 : %d\n", !1);           // 0
    printf("!42 : %d\n", !42);         // 0
    printf("!-1 : %d\n", !-1);         // 0
    
    return 0;
}

3. 短路求值

#include <stdio.h>

int GetValue(const char* name, int value) {
    printf("  [调用 GetValue(\"%s\") = %d]\n", name, value);
    return value;
}

int main() {
    printf("=== 短路求值 ===\n\n");
    
    // 逻辑与的短路
    printf("【逻辑与短路】\n");
    printf("测试: 0 && GetValue(\"B\", 1)\n");
    int r1 = GetValue("A", 0) && GetValue("B", 1);
    printf("结果: %d (第一个为0,不计算第二个)\n\n", r1);
    
    printf("测试: 1 && GetValue(\"B\", 1)\n");
    int r2 = GetValue("A", 1) && GetValue("B", 1);
    printf("结果: %d (第一个为1,需要计算第二个)\n\n", r2);
    
    // 逻辑或的短路
    printf("【逻辑或短路】\n");
    printf("测试: 1 || GetValue(\"B\", 0)\n");
    int r3 = GetValue("A", 1) || GetValue("B", 0);
    printf("结果: %d (第一个为1,不计算第二个)\n\n", r3);
    
    printf("测试: 0 || GetValue(\"B\", 1)\n");
    int r4 = GetValue("A", 0) || GetValue("B", 1);
    printf("结果: %d (第一个为0,需要计算第二个)\n", r4);
    
    return 0;
}

4. 短路求值的实用场景

#include <stdio.h>
#include <windows.h>

int main() {
    printf("=== 短路求值应用 ===\n\n");
    
    // 1. 空指针保护
    printf("【空指针保护】\n");
    char* str = NULL;
    // 如果str为NULL,不会调用strlen
    if (str && strlen(str) > 0) {
        printf("字符串不为空\n");
    } else {
        printf("字符串为空或NULL\n");
    }
    
    // 2. 句柄有效性检查
    printf("\n【句柄检查】\n");
    HANDLE hFile = INVALID_HANDLE_VALUE;
    // 句柄无效时不会调用CloseHandle
    if (hFile != INVALID_HANDLE_VALUE && CloseHandle(hFile)) {
        printf("句柄关闭成功\n");
    } else {
        printf("句柄无效或关闭失败\n");
    }
    
    // 3. 默认值模式
    printf("\n【默认值模式】\n");
    int userValue = 0;
    int defaultValue = 100;
    int result = userValue ? userValue : defaultValue;
    // 或者用逻辑或
    // int result = userValue || defaultValue; // 注意:这样结果是0或1
    printf("用户值: %d, 结果: %d\n", userValue, result);
    
    // 4. 安全的链式调用
    printf("\n【安全链式调用】\n");
    int arr[5] = {1, 2, 3, 4, 5};
    int index = 10;  // 越界索引
    int arrSize = 5;
    
    // 先检查越界,再访问数组
    if (index >= 0 && index < arrSize && arr[index] > 0) {
        printf("有效的正数\n");
    } else {
        printf("无效索引或非正数\n");
    }
    
    return 0;
}

5. 复杂条件表达式

#include <stdio.h>

// 检查是否是有效的用户名字符
int IsValidNameChar(char c) {
    return (c >= 'a' && c <= 'z') ||
           (c >= 'A' && c <= 'Z') ||
           (c >= '0' && c <= '9') ||
           c == '_';
}

// 检查是否是闰年
int IsLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 检查是否在范围内
int InRange(int value, int min, int max) {
    return value >= min && value <= max;
}

int main() {
    printf("=== 复杂条件表达式 ===\n\n");
    
    // 测试用户名字符
    printf("【用户名字符检查】\n");
    printf("'a' 有效: %d\n", IsValidNameChar('a'));
    printf("'5' 有效: %d\n", IsValidNameChar('5'));
    printf("'_' 有效: %d\n", IsValidNameChar('_'));
    printf("'@' 有效: %d\n", IsValidNameChar('@'));
    
    // 测试闰年
    printf("\n【闰年检查】\n");
    printf("2000 闰年: %d\n", IsLeapYear(2000));
    printf("2024 闰年: %d\n", IsLeapYear(2024));
    printf("1900 闰年: %d\n", IsLeapYear(1900));
    printf("2023 闰年: %d\n", IsLeapYear(2023));
    
    // 测试范围
    printf("\n【范围检查】\n");
    printf("50 在 [0,100]: %d\n", InRange(50, 0, 100));
    printf("150 在 [0,100]: %d\n", InRange(150, 0, 100));
    
    return 0;
}

6、课后作业

6.1、作业1:简化条件

将以下代码简化:

if (x > 0) {
    if (x < 100) {
        result = 1;
    } else {
        result = 0;
    }
} else {
    result = 0;
}

6.2、作业2:短路求值应用

使用短路求值实现安全的字符串长度检查函数:

int SafeStrlen(const char* str);
// 如果str为NULL返回0,否则返回实际长度

6.3、作业3:密码验证

实现密码强度检查函数,要求:

  • 长度至少8位
  • 包含大写字母
  • 包含小写字母
  • 包含数字