C语言学习笔记(十):操作符

1. 操作符概述

1.1 什么是操作符?

操作符(Operator)是C语言中用于执行各种操作的符号。它们是构成表达式的基本元素,告诉编译器对数据进行什么样的运算。

1.2 操作符的分类

分类 包含的操作符 优先级

算术操作符 + - * / % 较高

移位操作符 << >> 中等

位操作符 & \| ^ ~ 中等

赋值操作符 = += -= 等 较低

关系操作符 > < >= <= == != 较低

逻辑操作符 && \|\| ! 很低

条件操作符 ? : 很低

逗号操作符 , 最低

2. 算术操作符

2.1 基本算术操作符

复制代码
#include <stdio.h>



int main() {

    int a = 10, b = 3;

    

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

    

    // 加法

    printf("加法 (+) : a + b = %d\n", a + b);

    

    // 减法

    printf("减法 (-) : a - b = %d\n", a - b);

    

    // 乘法

    printf("乘法 (*) : a * b = %d\n", a * b);

    

    // 除法

    printf("除法 (/) : a / b = %d (整数除法,截断小数部分)\n", a / b);

    printf(" : (double)a / b = %.2f (浮点数除法)\n", (double)a / b);

    

    // 取模(求余数)

    printf("取模 (%%) : a %% b = %d (余数)\n", a % b);

    

    return 0;

}

运行结果:

a = 10, b = 3

加法 (+) : a + b = 13

减法 (-) : a - b = 7

乘法 (*) : a * b = 30

除法 (/) : a / b = 3 (整数除法,截断小数部分)

: (double)a / b = 3.33 (浮点数除法)

取模 (%) : a % b = 1 (余数)

2.2 重要注意事项

// ⚠️ 注意1:整数除法和浮点数除法

int x = 5, y = 2;

float result1 = x / y; // result1 = 2.0 (先整数除法,再转浮点)

float result2 = (float)x / y; // result2 = 2.5 (正确的浮点数除法)

float result3 = x / (float)y; // result3 = 2.5

float result4 = 5.0 / 2; // result4 = 2.5

// ⚠️ 注意2:取模操作符的操作数必须是整数

int mod1 = 10 % 3; // ✅ 正确,结果是1

// int mod2 = 10 % 3.0; // ❌ 错误,取模不能用于浮点数

// ⚠️ 注意3:除数为0

// int div = 10 / 0; // ❌ 运行时错误,除法零错误

3. 移位操作符

移位操作符用于将二进制位向左或向右移动。

3.1 左移操作符 <<

复制代码
#include <stdio.h>



int main() {

    unsigned int num = 5; // 二进制:0000 0101

    

    printf("原始数: %u, 二进制: 0000 0101\n", num);

    printf("左移1位: %u << 1 = %u, 二进制: 0000 1010\n", num, num << 1);

    printf("左移2位: %u << 2 = %u, 二进制: 0001 0100\n", num, num << 2);

    printf("左移3位: %u << 3 = %u, 二进制: 0010 1000\n", num, num << 3);

    

    // 左移相当于乘以2的幂

    printf("\n左移n位相当于乘以2^n\n");

    printf("5 << 1 = %d, 5 * 2 = %d\n", 5 << 1, 5 * 2);

    printf("5 << 3 = %d, 5 * 8 = %d\n", 5 << 3, 5 * 8);

    

    return 0;

}

3.2 右移操作符 >>

复制代码
#include <stdio.h>



int main() {

    unsigned int num = 20; // 二进制:0001 0100

    

    printf("原始数: %u, 二进制: 0001 0100\n", num);

    printf("右移1位: %u >> 1 = %u, 二进制: 0000 1010\n", num, num >> 1);

    printf("右移2位: %u >> 2 = %u, 二进制: 0000 0101\n", num, num >> 2);

    printf("右移3位: %u >> 3 = %u, 二进制: 0000 0010\n", num, num >> 3);

    

    // 右移相当于除以2的幂

    printf("\n右移n位相当于除以2^n\n");

    printf("20 >> 1 = %d, 20 / 2 = %d\n", 20 >> 1, 20 / 2);

    printf("20 >> 2 = %d, 20 / 4 = %d\n", 20 >> 2, 20 / 4);

    

    return 0;

}

3.3 有符号数的右移(算术右移 vs 逻辑右移)

复制代码
#include <stdio.h>



int main() {

    // 有符号负数

    int negative = -8; // 补码表示:1111 1000

    

    printf("有符号负数右移(算术右移):\n");

    printf("-8 >> 1 = %d\n", negative >> 1); // 结果为 -4

    

    // 无符号数右移(逻辑右移)

    unsigned int positive = 0x80000000;

    printf("\n无符号数右移(逻辑右移):\n");

    printf("0x80000000 >> 1 = 0x%x\n", positive >> 1);

    

    return 0;

}

4. 位操作符

位操作符直接对整数的二进制位进行操作。

4.1 位操作符详解

复制代码
#include <stdio.h>



void printBinary(unsigned int num) {

    for (int i = 31; i >= 0; i--) {

        printf("%d", (num >> i) & 1);

        if (i % 4 == 0) printf(" ");

    }

    printf("\n");

}



int main() {

    unsigned int a = 0b10101010; // 二进制:1010 1010 (170)

    unsigned int b = 0b11001100; // 二进制:1100 1100 (204)

    

    printf("a = %u, 二进制: ", a);

    printBinary(a);

    printf("b = %u, 二进制: ", b);

    printBinary(b);

    

    // 按位与 &

    printf("\n按位与 (&):\n");

    printf("a & b = %u, 二进制: ", a & b);

    printBinary(a & b);

    // 对应位都为1时结果为1,否则为0

    

    // 按位或 |

    printf("\n按位或 (|):\n");

    printf("a | b = %u, 二进制: ", a | b);

    printBinary(a | b);

    // 对应位至少有一个为1时结果为1

    

    // 按位异或 ^

    printf("\n按位异或 (^):\n");

    printf("a ^ b = %u, 二进制: ", a ^ b);

    printBinary(a ^ b);

    // 对应位不同时结果为1,相同时为0

    

    // 按位取反 ~

    printf("\n按位取反 (~):\n");

    printf("~a = %u, 二进制: ", ~a);

    printBinary(~a);

    // 每一位取反,0变1,1变0

    

    return 0;

}

4.2 位操作符的实用技巧

复制代码
#include <stdio.h>



int main() {

    unsigned int num = 0b10101010; // 170

    

    // 1. 判断奇偶性(最低位为1是奇数)

    if (num & 1) {

        printf("%d 是奇数\n", num);

    } else {

        printf("%d 是偶数\n", num);

    }

    

    // 2. 获取第n位的值(n从0开始)

    int n = 3;

    int bit = (num >> n) & 1;

    printf("第%d位的值是: %d\n", n, bit);

    

    // 3. 设置第n位为1

    num = num | (1 << n);

    printf("设置第%d位后: %d\n", n, num);

    

    // 4. 清除第n位(设置为0)

    num = num & ~(1 << n);

    printf("清除第%d位后: %d\n", n, num);

    

    // 5. 翻转第n位

    num = num ^ (1 << n);

    printf("翻转第%d位后: %d\n", n, num);

    

    // 6. 判断是否为2的幂

    unsigned int power = 16;

    if (power && !(power & (power - 1))) {

        printf("%d 是2的幂\n", power);

    } else {

        printf("%d 不是2的幂\n", power);

    }

    

    return 0;

}

5. 赋值操作符

5.1 基本赋值

int a = 10; // 简单赋值

int b, c;

b = c = 20; // 链式赋值,从右向左执行

5.2 复合赋值操作符

复制代码
#include <stdio.h>

int main() {

    int x = 10;  

    printf("初始值: x = %d\n\n", x);

    

    x += 5; // 等价于 x = x + 5

    printf("x += 5 → x = %d\n", x);

    

    x -= 3; // 等价于 x = x - 3

    printf("x -= 3 → x = %d\n", x);

    

    x *= 2; // 等价于 x = x * 2

    printf("x *= 2 → x = %d\n", x);

    

    x /= 4; // 等价于 x = x / 4

    printf("x /= 4 → x = %d\n", x);

    

    x %= 3; // 等价于 x = x % 3

    printf("x %%= 3 → x = %d\n", x);

    

    x <<= 2; // 等价于 x = x << 2

    printf("x <<= 2 → x = %d\n", x);

    

    x >>= 1; // 等价于 x = x >> 1

    printf("x >>= 1 → x = %d\n", x);

    

    x &= 3; // 等价于 x = x & 3

    printf("x &= 3 → x = %d\n", x);

    

    x |= 5; // 等价于 x = x | 5

    printf("x |= 5 → x = %d\n", x);

    

    x ^= 7; // 等价于 x = x ^ 7

    printf("x ^= 7 → x = %d\n", x);

    

    return 0;

}

6. 关系操作符

关系操作符用于比较两个值的大小关系,返回1(真)或0(假)。

复制代码
#include <stdio.h>



int main() {

    int a = 10, b = 20, c = 10;

    

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

    

    printf("a > b : %d\n", a > b); // 大于

    printf("a < b : %d\n", a < b); // 小于

    printf("a >= c : %d\n", a >= c); // 大于等于

    printf("a <= c : %d\n", a <= c); // 小于等于

    printf("a == c : %d\n", a == c); // 等于

    printf("a != b : %d\n", a != b); // 不等于

    

    // ⚠️ 常见错误:将 == 误写为 =

    if (a = 5) { // 这不是比较,而是赋值!条件永远为真

        printf("\n⚠️ 警告:这里误将 == 写成了 =\n");

    }

    

    // 正确写法

    if (a == 5) {

        printf("a 等于 5\n");

    }

    

    return 0;

}

7. 逻辑操作符

逻辑操作符用于组合多个条件表达式。

复制代码
#include <stdio.h>

#include <stdbool.h>



int main() {

    int age = 25;

    bool hasLicense = true;

    bool hasInsurance = false;

    

    printf("年龄: %d\n", age);

    printf("有驾照: %s\n", hasLicense ? "是" : "否");

    printf("有保险: %s\n\n", hasInsurance ? "是" : "否");

    

    // 逻辑与 (&&): 两个条件都为真时结果为真

    if (age >= 18 && hasLicense) {

        printf("✅ 可以驾驶(年龄>=18且有驾照)\n");

    } else {

        printf("❌ 不能驾驶\n");

    }

    

    // 逻辑或 (||): 至少一个条件为真时结果为真

    if (hasLicense || hasInsurance) {

        printf("✅ 至少满足一项要求\n");

    } else {

        printf("❌ 两项都不满足\n");

    }

    

    // 逻辑非 (!): 取反

    if (!hasInsurance) {

        printf("⚠️ 没有保险,请注意安全!\n");

    }

    

    // 短路求值特性

    int x = 0;

    if (x != 0 && 10 / x > 1) { // 短路:x != 0为假,不会执行10/x

        printf("这行不会执行\n");

    }

    printf("短路求值避免了除零错误\n");

    

    return 0;

}

短路求值详解

复制代码
#include <stdio.h>



int test1() {

    printf("test1 被调用\n");

    return 0;

}



int test2() {

    printf("test2 被调用\n");

    return 1;

}



int main() {

    printf("=== 逻辑与短路 ===\n");

    if (test1() && test2()) {

        // test1()返回0,test2()不会被调用

        printf("条件为真\n");

    } else {

        printf("条件为假\n");

    }

    

    printf("\n=== 逻辑或短路 ===\n");

    if (test2() || test1()) {

        // test2()返回1,test1()不会被调用

        printf("条件为真\n");

    } else {

        printf("条件为假\n");

    }

    

    return 0;

}

8. 条件操作符(三目操作符)

条件操作符 ? : 是C语言中唯一的三目操作符,可以简化if-else语句。

复制代码
#include <stdio.h>



int main() {

    int score = 85;

    

    // 基本用法

    char grade = (score >= 90) ? 'A' : (score >= 80) ? 'B' : (score >= 70) ? 'C' : 'F';

    printf("分数: %d, 等级: %c\n", score, grade);

    

    // 嵌套使用

    int a = 10, b = 20;

    int max = (a > b) ? a : b;

    printf("最大值: %d\n", max);

    

    // 在输出中使用

    printf("a %s b\n", (a > b) ? "大于" : (a < b) ? "小于" : "等于");

    

    // ⚠️ 注意:条件操作符不是if语句的完全替代品

    // 以下情况使用if更清晰

    int x = 5;

    if (x > 0) {

        printf("正数\n");

    } else if (x < 0) {

        printf("负数\n");

    } else {

        printf("零\n");

    }

    

    return 0;

}

9. 逗号操作符

逗号操作符用于将多个表达式组合成一个表达式,按从左到右的顺序执行,整个表达式的值是最后一个表达式的值。

复制代码
#include <stdio.h>



int main() {

    int a, b, c;

    

    // 逗号操作符在for循环中的应用

    printf("逗号操作符在for循环中:\n");

    for (a = 0, b = 10; a < b; a++, b--) {

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

    }

    

    // 逗号操作符的值

    printf("\n逗号操作符的值:\n");

    int result = (a = 3, b = 5, c = a + b);

    printf("(a=3, b=5, c=a+b) = %d\n", result);

    

    // 在函数调用中的使用(注意区分)

    // printf("%d, %d", (x, y), z); // 这里逗号是操作符

    // printf("%d, %d", x, y); // 这里逗号是分隔符

    

    return 0;

}

10. 操作符优先级和结合性

10.1 优先级表

复制代码
#include <stdio.h>



int main() {

    // 优先级决定了操作数的结合顺序

    int a = 5, b = 10, c = 15;

    

    // 乘除优先级高于加减

    int result1 = a + b * c; // 等价于 a + (b * c)

    int result2 = (a + b) * c; // 括号改变优先级

    

    printf("a + b * c = %d\n", result1); // 5 + 150 = 155

    printf("(a + b) * c = %d\n", result2); // 15 * 15 = 225

    

    // 逻辑操作符优先级

    int x = 1, y = 2, z = 3;

    int result3 = x < y && y < z; // 等价于 (x < y) && (y < z)

    printf("x < y && y < z = %d\n", result3);

    

    // 常见优先级问题

    int result4 = a > b ? a : b; // 等价于 (a > b) ? a : b

    // int result5 = a > b ? a : b = 10; // 错误!优先级问题

    

    return 0;

}

10.2 优先级完整列表(从高到低)

优先级 操作符 描述 结合性

1 () [] . -> 括号、下标、成员 左→右

2 ++ -- + - ! ~ * & (type) 单目操作符 右→左

3 * / % 乘除取模 左→右

4 + - 加减 左→右

5 << >> 移位 左→右

6 < <= > >= 关系 左→右

7 == != 相等性 左→右

8 & 按位与 左→右

9 ^ 按位异或 左→右

10 \| 按位或 左→右

11 && 逻辑与 左→右

12 \|\| 逻辑或 左→右

13 ?: 条件 右→左

14 = += -= 等 赋值 右→左

15 , 逗号 左→右

11. sizeof 操作符

sizeof 是一个编译时操作符,用于计算数据类型或变量所占的内存字节数。

复制代码
#include <stdio.h>



int main() {

    // 基本数据类型的大小

    printf("=== 基本数据类型大小 ===\n");

    printf("sizeof(char) = %zu 字节\n", sizeof(char));

    printf("sizeof(short) = %zu 字节\n", sizeof(short));

    printf("sizeof(int) = %zu 字节\n", sizeof(int));

    printf("sizeof(long) = %zu 字节\n", sizeof(long));

    printf("sizeof(long long)= %zu 字节\n", sizeof(long long));

    printf("sizeof(float) = %zu 字节\n", sizeof(float));

    printf("sizeof(double) = %zu 字节\n", sizeof(double));

    

    // 变量的大小

    int arr[10];

    printf("\n=== 变量大小 ===\n");

    printf("sizeof(arr) = %zu 字节\n", sizeof(arr));

    printf("sizeof(arr[0]) = %zu 字节\n", sizeof(arr[0]));

    printf("数组元素个数 = %zu\n", sizeof(arr) / sizeof(arr[0]));

    

    // 指针的大小

    int *ptr;

    printf("\n=== 指针大小 ===\n");

    printf("sizeof(ptr) = %zu 字节\n", sizeof(ptr));

    

    // 字符串常量

    printf("\n=== 字符串常量 ===\n");

    printf("sizeof(\"hello\") = %zu (包括结尾的'\\0')\n", sizeof("hello"));

    

    return 0;

}

12. 类型转换

12.1 隐式类型转换

复制代码
#include <stdio.h>



int main() {

    // 1. 算术转换:将较小类型提升为较大类型

    int a = 10;

    double b = 3.14;

    double result = a + b; // a 自动转换为 double

    printf("10 + 3.14 = %.2f\n", result);

    

    // 2. 赋值转换

    int x = 3.14; // double 转换为 int,小数部分截断

    printf("int x = 3.14 → x = %d\n", x);

    

    // 3. 函数参数传递

    printf("sizeof('A') = %zu\n", sizeof('A')); // 字符常量被提升为 int

    

    // 4. 整型提升

    char c1 = 100, c2 = 200;

    int sum = c1 + c2; // char 提升为 int 再计算

    printf("100 + 200 = %d\n", sum);

    

    return 0;

}

12.2 显式类型转换(强制类型转换)

复制代码
#include <stdio.h>



int main() {

    int a = 10, b = 3;

    

    // 强制转换为浮点数进行精确除法

    float result1 = (float)a / b;

    printf("(float)10 / 3 = %.2f\n", result1);

    

    // 指针类型转换

    int num = 0x12345678;

    unsigned char *ptr = (unsigned char*)&num;

    printf("整数的字节表示(小端序):\n");

    for (int i = 0; i < sizeof(num); i++) {

        printf("字节%d: 0x%02x\n", i, ptr[i]);

    }

    

    // 避免数据丢失的警告

    long long bigNum = 0x123456789ABCDEF0;

    int smallNum = (int)bigNum; // 显式转换,可能丢失数据

    printf("大数转小数: 0x%llx → 0x%x\n", bigNum, smallNum);

    

    return 0;

}

13. 综合练习

练习1:位操作应用 - 简单加密

复制代码
#include <stdio.h>

#include <string.h>



// 简单的异或加密

void xorEncrypt(char *data, char key, size_t len) {

    for (size_t i = 0; i < len; i++) {

        data[i] ^= key; // 异或操作

    }

}



int main() {

    char message[] = "Hello, World!";

    char key = 0x5A; // 密钥

    size_t len = strlen(message);

    

    printf("原始消息: %s\n", message);

    

    // 加密

    xorEncrypt(message, key, len);

    printf("加密后: ");

    for (size_t i = 0; i < len; i++) {

        printf("%02X ", (unsigned char)message[i]);

    }

    printf("\n");

    

    // 解密(相同的操作)

    xorEncrypt(message, key, len);

    printf("解密后: %s\n", message);

    

    return 0;

}

练习2:操作符优先级陷阱

复制代码
#include <stdio.h>



int main() {

    int a = 1, b = 2, c = 3;

    

    // 陷阱1:逻辑与 vs 按位与

    if (a & b) { // 按位与,结果为0

        printf("a & b 为真\n");

    } else {

        printf("a & b 为假\n");

    }

    

    if (a && b) { // 逻辑与,结果为真

        printf("a && b 为真\n");

    }

    

    // 陷阱2:赋值 vs 比较

    int x = 5;

    if (x = 10) { // 赋值操作,条件为真

        printf("x = %d (这是赋值,不是比较)\n", x);

    }

    

    // 陷阱3:优先级导致的错误

    int result = a + b > c ? a : b; // 等价于 (a + b > c) ? a : b

    printf("result = %d\n", result);

    

    return 0;

}

14. 常见错误和最佳实践

14.1 常见错误

// ❌ 错误1:混淆 = 和 ==

if (x = 5) { // 应该是 x == 5

// 这个条件永远为真

}

// ❌ 错误2:位操作符和逻辑操作符混淆

if (x & y) // 按位与

if (x && y) // 逻辑与

// ❌ 错误3:忽视优先级

int result = a << 1 + b; // 实际是 a << (1 + b),不是 (a << 1) + b

// ❌ 错误4:自增自运算符的副作用

int i = 0;

int arr[] = {1, 2, 3};

int idx = 0;

// arr[idx++] = arr[idx]; // 未定义行为

arr[idx] = arr[idx + 1]; // 正确写法

idx++;

14.2 最佳实践

// ✅ 1. 使用括号明确优先级

int result = (a << 1) + b; // 清晰明了

// ✅ 2. 避免使用逗号操作符(除了for循环)

// 不好:x = (a++, b++, a + b);

// 好:a++; b++; x = a + b;

// ✅ 3. 位操作时使用无符号类型

unsigned int flags = 0;

flags |= (1 << 3); // 设置第3位

// ✅ 4. 使用 sizeof 时注意类型

int arr[10];

int size = sizeof(arr) / sizeof(arr[0]); // 正确

// int size = sizeof(arr) / sizeof(int); // 也可以,但容易出错

// ✅ 5. 检查除零和取模运算

if (y != 0) {

result = x / y;

mod = x % y;

}

15. 总结

操作符类别 关键点 常见用途

算术 注意整数除法截断 数学计算

移位 左移乘2,右移除2 高效乘除运算、位标志操作

位操作 直接操作二进制位 标志位、权限控制、加密

赋值 复合赋值简洁高效 变量更新

关系 返回0或1 条件判断

逻辑 短路求值特性 复杂条件组合

条件 简化if-else 简单条件赋值

逗号 组合表达式 for循环

核心要点:

  1. 理解每个操作符的功能和优先级

  2. 使用括号明确运算顺序

  3. 区分 = 和 ==

  4. 注意整数除法和浮点数除法的区别

  5. 合理使用位操作提高效率

  6. 警惕未定义行为

学习建议:

· 多写代码实践,体会操作符的行为

· 使用 printf 输出中间结果验证理解

· 在复杂表达式中善用括号

· 阅读优秀代码,学习他人的写法

记住:掌握操作符是写出高效、正确C代码的基础!

相关推荐
鹭天2 小时前
RAG学习笔记
笔记·学习
myloveasuka2 小时前
[Java]单列集合
android·java·开发语言
南梦浅2 小时前
全过程步骤(从零到高可用企业网络)
开发语言·网络·php
mjhcsp2 小时前
C++ 梯度下降法(Gradient Descent):数值优化的核心迭代算法
开发语言·c++·算法
ok_hahaha2 小时前
java从头开始-黑马点评-基础篇
java·开发语言
吴声子夜歌2 小时前
JavaScript——函数
开发语言·javascript·ecmascript
yunyun321232 小时前
跨语言调用C++接口
开发语言·c++·算法
恒拓高科WorkPlus2 小时前
BeeWorks:高效安全,重塑企业即时通信新体验 - BeeWorks
经验分享
m0_518019482 小时前
C++中的装饰器模式变体
开发语言·c++·算法