✨ 千呼万唤始出来!C语言操作符系列第二篇重磅更新~ 上一篇我们搞定了操作符分类、进制转换和补码这些“地基知识”,这篇直接上干货满满的实战内容!移位操作符、位操作符(嵌入式/算法竞赛的香饽饽🔥)、逗号表达式,还有天天用的数组/函数调用操作符,每个知识点都配了可直接复制运行的代码,看完就能上手实操,小白也能轻松拿捏!

一、移位操作符:<< 左移 和 >> 右移 🚀

移位操作符是专门“摆弄”整数二进制补码的工具,有两个铁律必须记住:只对整数生效不能移负数位(比如写num >> -1,编译器会直接报错哦❌)。

1. 左移操作符 <<:左边丢,右边补0

左移的逻辑超简单,就像队列整体移动——把整数的二进制补码向左挪n位,左边超出的n位直接“扔掉”,右边空出来的位置统一补0。

  • 核心规则:左移n位 = 舍弃左边n位 + 右边补n个0

  • 黄金小规律:无溢出时,左移1位 ≈ 乘以2,左移n位 ≈ 乘以2ⁿ(计算效率比乘法高多啦⚡)

代码示例:左移1位实战

#include <stdio.h>
int main()
{
    int a = 10; // 二进制补码:00000000 00000000 00000000 00001010
    int b = a << 1; // 左移1位:右边补0 → 00000000...00010100(对应十进制20)
    printf("a=%d, b=%d\n", a, b); // 输出结果:a=10, b=20
    return 0;
}

左移不会修改原变量的值!示例中a始终是10,b只是接收了左移后的新结果~

2. 右移操作符 >>:算术右移是主流

右移分两种,但不用纠结——VS、GCC这些主流编译器都用算术右移,逻辑右移基本用不上。

  • 算术右移规则:左边补符号位(正数补0,负数补1),右边丢n位(重点中的重点✅)

  • 逻辑右移规则:左边补0,右边丢n位(仅作了解即可)

  • 黄金小规律:无溢出时,右移1位 ≈ 除以2(向下取整),比如5右移1位是2,-5右移1位是-3

代码示例1:正数右移

#include <stdio.h>
int main()
{
    int a = 10; // 补码:00000000...00001010(符号位为0)
    int b = a >> 1; // 算术右移1位:左边补0 → 00000000...00000101(对应十进制5)
    printf("a=%d, b=%d\n", a, b); // 输出结果:a=10, b=5
    return 0;
}

代码示例2:负数右移(符号位是关键)

#include <stdio.h>
int main()
{
    int a = -10; // 补码:11111111...11110110(符号位为1)
    int b = a >> 1; // 算术右移1位:左边补1 → 11111111...11111011(对应十进制-5)
    printf("a=%d, b=%d\n", a, b); // 输出结果:a=-10, b=-5
    return 0;
}

二、位操作符:& | ^ ~(二进制位的魔术师🎩)

位操作符直接对二进制补码的每一位动手,操作数必须是整数。嵌入式开发配置寄存器、算法题优化性能,全靠它撑场面!四个操作符咱们逐个拆解~

1. 按位与 &:有0则0,全1才1

按位与就像“严格的门禁”——两个数的二进制位逐位对比,只要有一个位是0,结果就“放行”0;只有两个位都是1,才“放行”1。

  • 核心规则:逐位运算,有0出0,全1出1

  • 高频场景
    判断奇偶数:n & 1,结果1是奇数,0是偶数(超实用!)

  • 指定位置0:把要置0的位和0对齐,其余位和1对齐,按位与即可

代码示例:按位与运算

#include <stdio.h>
int main()
{
    int a = -8; // 补码:11111111...11111000
    int b = 6;  // 补码:00000000...00000110
    int c = a & b; // 逐位与:每一位都有0 → 00000000...00000000(对应十进制0)
    printf("c=%d\n", c); // 输出结果:c=0
    return 0;
}

2. 按位或 |:有1则1,全0才0

按位或和按位与相反,像“宽松的门禁”——只要有一个位是1,结果就“放行”1;只有两个位都是0,才“放行”0。

  • 核心规则:逐位运算,有1出1,全0出0

  • 高频场景:指定位置1(把要置1的位和1对齐,其余位和0对齐,按位或即可)

代码示例:按位或运算

#include <stdio.h>
int main()
{
    int a = -8; // 补码:11111111...11111000
    int b = 6;  // 补码:00000000...00000110
    int c = a | b; // 逐位或:每一位有1 → 11111111...11111110(对应十进制-2)
    printf("c=%d\n", c); // 输出结果:c=-2
    return 0;
}

3. 按位异或 ^:相同为0,相异为1

按位异或是位操作符里的“明星”,规则好记,特性超强,是很多算法题的解题关键!

  • 核心规则:逐位运算,相同出0,相异出1

  • 三大核心特性(必须背会🌟)
    a ^ a = 0:自己和自己异或,结果必为0

  • a ^ 0 = a:和0异或,结果还是自己

  • 交换律:a ^ b = b ^ a

  • 高频场景:不创建临时变量交换两个整数(无溢出风险,比加减法安全)

代码示例:异或交换变量(经典面试题)

#include <stdio.h>
int main()
{
    int a = 3, b = 5;
    printf("交换前:a=%d, b=%d\n", a, b); // 初始:a=3, b=5
    
    a = a ^ b; // 第一步:a = 3^5(存下两者的异或结果)
    b = a ^ b; // 第二步:b = (3^5)^5 = 3(利用a^a=0,5^5=0,剩下3)
    a = a ^ b; // 第三步:a = (3^5)^3 = 5(同理,3^3=0,剩下5)
    
    printf("交换后:a=%d, b=%d\n", a, b); // 结果:a=5, b=3
    return 0;
}

4. 按位取反 ~:0变1,1变0

按位取反是“反转大师”,把二进制补码的每一位(包括符号位)都反过来,0变1,1变0。

  • 核心规则:逐位反转,0→1,1→0(符号位不例外)

  • 关键提醒:取反结果必为负数!因为符号位会从0(正数)变1,或从1(负数)变0后,整体数值必然是负数。

代码示例:按位取反运算

#include <stdio.h>
int main()
{
    int a = -1; // 补码:11111111...11111111(全1)
    int b = ~a; // 取反后:00000000...00000000(对应十进制0)
    printf("b=%d\n", b); // 输出结果:b=0
    return 0;
}

实战练习:统计二进制中1的个数(面试必考题🔥)

n & (n-1)的技巧,每次运算能“擦掉”n二进制中最右边的一个1,循环几次就有几个1,效率拉满!

#include <stdio.h>
int main()
{
    int n = 0, count = 0;
    printf("请输入一个整数:");
    scanf("%d", &n);
    
    while (n) { // n不为0就继续
        n = n & (n - 1); // 擦掉最右边的1
        count++; // 计数+1
    }
    
    printf("二进制中1的个数:%d\n", count);
    return 0;
}

比如输入15(二进制1111),循环4次输出4;输入10(二进制1010),循环2次输出2,超精准!

三、逗号表达式:, 从左到右,取最后一个结果 📝

逗号表达式是“代码简化神器”,用逗号把多个表达式串起来,从左到右依次执行,但整个表达式的结果只看最后一个。

  • 核心规则:先执行左边所有表达式,最终结果 = 最后一个表达式的值

  • 高频场景:把多个操作合并成一个表达式,比如简化循环条件

代码示例:逗号表达式实战

#include <stdio.h>
int main()
{
    int a = 1, b = 2;
    // 逗号表达式:依次执行a>b、a=b+10、a、b=a+1,c取最后一个结果
    int c = (a > b, a = b + 10, a, b = a + 1); 
    // 执行过程:1.判断a>b(假);2.a=12;3.取a(无用);4.b=13 → c=13
    printf("c=%d\n", c); // 输出结果:c=13
    
    // 循环条件简化:先取值、计数,再判断
    int x = 0;
    while (x = get_val(), count_val(x), x > 0) {
        // 业务处理
    }
    return 0;
}

逗号表达式的括号不能省!没括号会因优先级问题出错,一定要注意~

四、下标引用 & 函数调用操作符:[] 和 () 🛠️

这两个操作符天天用,可能你没意识到它们也是“正牌操作符”~ 它们都是多操作数操作符,操作数数量不固定。

1. 下标引用操作符 []:数组访问专属

数组访问用的[]就是下标引用操作符,操作数是数组名(如arr)和下标(如i),格式为arr[i]

  • 核心规则arr[i] = *(arr + i)(数组名是首元素地址,arr+i是第i个元素地址,解引用就得到值)

  • 致命提醒:下标从0开始!arr[10]的合法下标是0-9,越界会导致内存错误💥

代码示例:下标引用操作符

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    printf("%d\n", arr[4]); // 操作数arr和4 → 访问第5个元素(值为5)
    printf("%d\n", *(arr+4)); // 和arr[4]完全等价 → 输出5
    return 0;
}

2. 函数调用操作符 ():调用函数必备

调用函数时的()就是函数调用操作符,操作数至少有1个(函数名),后面可跟多个参数(用逗号分隔)。

  • 核心规则函数名(参数1, 参数2, ...),操作数 = 函数名 + 所有参数

  • 例子
    printf("hi"):操作数是printf和"hi"

  • add(3,5):操作数是add、3、5

代码示例:函数调用操作符

#include <stdio.h>
// 加法函数
int add(int x, int y) {
    return x + y;
}
// 无参函数
void test() {
    printf("函数调用成功✨\n");
}
int main()
{
    printf("Hello C!\n"); // 操作数:printf、"Hello C!" → 输出Hello C!
    int c = add(3, 5); // 操作数:add、3、5 → c=8
    test(); // 操作数:test → 输出函数调用成功✨
    printf("3+5=%d\n", c); // 输出3+5=8
    return 0;
}

写在最后

🎉 到这里,移位操作符、位操作符(这俩是重点,一定要多练)、逗号表达式,还有数组/函数调用操作符就全掌握啦!这些知识点不是纸上谈兵——移位和位操作符在嵌入式、算法中能解决很多实际问题,逗号表达式和调用操作符则是提升代码简洁性的基础。

下一篇我们将攻克更实用的内容:结构体成员访问操作符(.->,处理结构体必备)、操作符的优先级与结合性(避免踩优先级的坑),还有表达式求值的常见陷阱(很多人栽在这里!),记得持续关注哦~

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐