news 2026/4/25 16:18:24

C语言操作符深度解析:从基础到高级应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言操作符深度解析:从基础到高级应用

引言

操作符是C语言的灵魂,它们决定了数据的计算方式、逻辑判断和内存操作。理解操作符的优先级、结合性和使用规则,是写出正确、高效代码的基础。

C语言拥有丰富的操作符,包括算术操作符、移位操作符、位操作符、赋值操作符、逻辑操作符、条件操作符等。今天,我将从底层视角,全面讲解各类操作符的用法、注意事项以及常见陷阱。


第一部分:算术操作符

一、基本算术操作符

操作符名称示例说明
+加法a + b两数相加
-减法a - b两数相减
*乘法a * b两数相乘
/除法a / b两数相除(整数除法截断)
%取模a % b取余数,操作数必须为整数

二、注意事项

#include <stdio.h> int main() { // 整数除法:结果截断,不四舍五入 int a = 10 / 3; // 3,不是3.333 int b = 10 / 4; // 2 // 浮点数除法:至少有一个操作数为浮点数 double c = 10.0 / 3; // 3.333333 double d = 10 / 3.0; // 3.333333 // 取模操作符:操作数必须为整数 int e = 10 % 3; // 1 // double f = 10.0 % 3; // 错误!取模不能用于浮点数 // 负数取模:结果符号与被除数相同 int g = -10 % 3; // -1 int h = 10 % -3; // 1 int i = -10 % -3; // -1 printf("10 / 3 = %d\n", a); printf("10.0 / 3 = %lf\n", c); printf("-10 %% 3 = %d\n", g); return 0; }

第二部分:移位操作符

一、左移(<<)和右移(>>

移位操作符只针对整数类型(整型家族)。

操作符名称示例说明
<<左移a << n高位丢弃,低位补0
>>右移a >> n低位丢弃,高位补符号位(算术右移)或0(逻辑右移)
#include <stdio.h> int main() { // 左移:相当于乘以2的n次方 int a = 15; // 00000000 00000000 00000000 00001111 a <<= 7; // 00000000 00000000 00000111 10000000 = 1920 printf("15 << 7 = %d\n", a); // 1920 // 右移:相当于除以2的n次方(向下取整) int b = 320; // 00000000 00000000 00000001 01000000 b >>= 4; // 00000000 00000000 00000000 00010100 = 20 printf("320 >> 4 = %d\n", b); // 20 // 负数右移(算术右移,高位补1) int c = -10; // 11111111 11111111 11111111 11110110 c >>= 2; // 11111111 11111111 11111111 11111101 = -3 printf("-10 >> 2 = %d\n", c); // -3 return 0; }

二、移位操作的注意事项

int main() { int a = 10; // 1. 移位位数不能大于等于类型位数(未定义行为) // a << 32; // 在32位系统中是未定义行为 // 2. 不能移位负数位 // a << -1; // 未定义行为 // 3. 移位不会改变原变量的值(除非使用赋值操作符) int b = 10; int c = b << 2; // b不变,c=40 b <<= 2; // b变为40 return 0; }

第三部分:位操作符

一、位操作符分类

操作符名称示例说明
&按位与a & b两个都为1,结果为1
|按位或a | b两个都为0,结果为0
^按位异或a ^ b相同为0,不同为1
~按位取反~a0变1,1变0(单目操作符)

二、按位与(&

#include <stdio.h> int main() { // 判断奇偶数:与1按位与,结果为1是奇数,0是偶数 int a = 10; // 1010 int b = 7; // 0111 if (a & 1) { printf("%d 是奇数\n", a); // 不执行 } else { printf("%d 是偶数\n", a); // 执行 } // 将某一位清零(与0按位与) int c = 0xFF; // 11111111 int d = c & 0xFE; // 11111110,最低位清零 printf("0xFF & 0xFE = 0x%X\n", d); // 0xFE return 0; }

三、按位或(|

int main() { int a = 0b00001000; // 第3位为1 int b = 0b00000100; // 第2位为1 // 将某位置为1(与1按位或) int c = a | b; // 0b00001100,第2位和第3位都为1 // 将第4位置为1 int d = 0; // 00000000 d |= (1 << 4); // 00010000 printf("d = %d\n", d); // 16 return 0; }

四、按位异或(^

int main() { // 异或的特性: // a ^ a = 0 // a ^ 0 = a // a ^ b ^ b = a int a = 5; // 0101 int b = 3; // 0011 int c = a ^ b; // 0110 = 6 // 加密解密(相同密钥异或两次恢复原值) int plain = 123; int key = 456; int cipher = plain ^ key; // 加密 int decrypted = cipher ^ key; // 解密 printf("加密后:%d,解密后:%d\n", cipher, decrypted); return 0; }

五、按位取反(~

int main() { int a = 0; // 00000000 00000000 00000000 00000000 int b = ~a; // 11111111 11111111 11111111 11111111 = -1 int c = -1; // 11111111 11111111 11111111 11111111 int d = ~c; // 00000000 00000000 00000000 00000000 = 0 printf("~0 = %d\n", b); // -1 printf("~(-1) = %d\n", d); // 0 return 0; }

六、异或实现交换(无临时变量)

#include <stdio.h> // 方法1:使用临时变量(推荐,易懂) void swap1(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // 方法2:使用加减法(可能溢出) void swap2(int* a, int* b) { *a = *a + *b; *b = *a - *b; *a = *a - *b; } // 方法3:使用异或(无溢出风险,但只适用于整数) void swap3(int* a, int* b) { // a=5(101), b=3(011) *a = *a ^ *b; // a = 101 ^ 011 = 110 (6) *b = *a ^ *b; // b = 110 ^ 011 = 101 (5) *a = *a ^ *b; // a = 110 ^ 101 = 011 (3) } int main() { int x = 5, y = 3; printf("交换前:x=%d, y=%d\n", x, y); swap3(&x, &y); printf("交换后:x=%d, y=%d\n", x, y); return 0; }

第四部分:逻辑操作符

一、逻辑与(&&)和逻辑或(||

操作符名称示例说明
&&逻辑与a && b两边都为真,结果为真
||逻辑或a || b一边为真,结果为真
!逻辑非!a真变假,假变真

二、短路求值(重要!)

#include <stdio.h> int main() { int a = 0, b = 2, c = 3, d = 4; int i; // 逻辑与短路:左边为假,右边不执行 i = a++ && ++b && d++; // a++ = 0(假),整个表达式为假,++b和d++不执行 printf("i=%d, a=%d, b=%d, d=%d\n", i, a, b, d); // 输出:i=0, a=1, b=2, d=4 // 重置变量 a = 0, b = 2, c = 3, d = 4; // 逻辑或短路:左边为真,右边不执行 i = a++ || ++b || d++; // a++ = 0(假),继续执行++b = 3(真),整个表达式为真,d++不执行 printf("i=%d, a=%d, b=%d, d=%d\n", i, a, b, d); // 输出:i=1, a=1, b=3, d=4 printf("0 && 9 = %d\n", 0 && 9); // 0 printf("0 || 9 = %d\n", 0 || 9); // 1 return 0; }

第五部分:赋值操作符

一、复合赋值操作符

操作符示例等价于
+=a += ba = a + b
-=a -= ba = a - b
*=a *= ba = a * b
/=a /= ba = a / b
%=a %= ba = a % b
<<=a <<= na = a << n
>>=a >>= na = a >> n
&=a &= ba = a & b
|=a |= ba = a | b
^=a ^= ba = a ^ b

二、自增自减操作符

#include <stdio.h> int main() { int a = 10; int b, c; // 后置++:先使用,后自增 b = a++; // b = 10, a = 11 printf("a=%d, b=%d\n", a, b); // a=11, b=10 // 前置++:先自增,后使用 a = 10; c = ++a; // a = 11, c = 11 printf("a=%d, c=%d\n", a, c); // a=11, c=11 // 表达式中的副作用 int p = 0; int l = p++; // l = 0, p = 1 printf("l=%d, p=%d\n", l, p); // l=0, p=1 return 0; }

第六部分:关系操作符

操作符名称示例
>大于a > b
>=大于等于a >= b
<小于a < b
<=小于等于a <= b
==等于a == b
!=不等于a != b
int main() { int a = 10, b = 20; // 关系表达式的结果是整数:真为1,假为0 int r1 = a > b; // 0 int r2 = a < b; // 1 int r3 = a == b; // 0 printf("10 > 20 = %d\n", r1); printf("10 < 20 = %d\n", r2); return 0; }

第七部分:条件操作符(三目运算符)

一、语法与使用

// 语法:表达式1 ? 表达式2 : 表达式3 // 如果表达式1为真,返回表达式2,否则返回表达式3 #include <stdio.h> int main() { int a = 10, b = 20; // 求最大值 int max = (a > b) ? a : b; printf("max = %d\n", max); // 20 // 求绝对值 int x = -5; int abs_x = (x >= 0) ? x : -x; printf("|%d| = %d\n", x, abs_x); // 5 return 0; }

二、嵌套使用

int main() { int score = 85; // 成绩等级判断 char grade = (score >= 90) ? 'A' : (score >= 80) ? 'B' : (score >= 70) ? 'C' : (score >= 60) ? 'D' : 'F'; printf("成绩:%d,等级:%c\n", score, grade); // B return 0; }

第八部分:逗号操作符

一、语法与使用

逗号操作符从左到右依次执行所有表达式,返回最后一个表达式的值。

#include <stdio.h> int main() { int a, b, c; // 逗号操作符示例 a = (1, 2, 3, 4, 5); // a = 5 printf("a = %d\n", a); // 在循环中使用 for (int i = 0, j = 10; i < j; i++, j--) { printf("i=%d, j=%d\n", i, j); } return 0; }

第九部分:sizeof 操作符

一、sizeof 的特点

sizeof既是操作符也是关键字,在编译时计算类型或变量占用的字节数。

#include <stdio.h> int main() { int a = 10; int arr[10]; // sizeof 的三种用法 printf("sizeof(a) = %zu\n", sizeof(a)); // 4(使用变量) printf("sizeof(int) = %zu\n", sizeof(int)); // 4(使用类型) printf("sizeof a = %zu\n", sizeof a); // 4(省略括号) // 错误写法 // printf("%zu\n", sizeof int); // 错误!类型不能省略括号 // 计算数组元素个数 int len = sizeof(arr) / sizeof(arr[0]); printf("数组长度:%d\n", len); return 0; }

第十部分:类型转换

一、隐式类型转换(算术转换)

int main() { // 算术转换顺序(下转上) // long double ← double ← float ← unsigned long ← long ← unsigned int ← int int a = 10; double b = 3.14; double c = a + b; // a被自动转换为double // 截断:赋值时大类型转小类型 int d = 3.14; // d = 3,小数部分被截断 return 0; }

二、整型提升

int main() { char a = 0b10111111; // 0xBF char b = 0b10111111; // 0xBF // 运算时会发生整型提升:char → int int c = a + b; // 0xBF + 0xBF = 0x17E printf("%d\n", c); // 382 return 0; }

三、强制类型转换

int main() { int a = 10, b = 3; // 整数除法 double c = a / b; // 3.0 double d = (double)a / b; // 3.33333 double e = a / (double)b; // 3.33333 printf("c = %lf\n", c); printf("d = %lf\n", d); return 0; }

第十一部分:操作符优先级与结合性

一、优先级表(从高到低)

优先级操作符结合性
1()[].->从左到右
2++--+-!~*&sizeof(单目)从右到左
3*/%从左到右
4+-从左到右
5<<>>从左到右
6<<=>>=从左到右
7==!=从左到右
8&(按位与)从左到右
9^(按位异或)从左到右
10|(按位或)从左到右
11&&(逻辑与)从左到右
12||(逻辑或)从左到右
13?:(条件)从右到左
14=+=-=等 (赋值)从右到左
15,(逗号)从左到右

二、常见优先级陷阱

#include <stdio.h> int main() { int a = 10, b = 20; // 陷阱1:== 优先级高于 & 和 | if (a & b == 0) { // 实际:a & (b == 0),不是 (a & b) == 0 // ... } // 正确写法 if ((a & b) == 0) { // ... } // 陷阱2:移位优先级低于加减 int c = a + b << 2; // 实际:(a + b) << 2 printf("c = %d\n", c); // 陷阱3:逻辑操作符优先级低于关系操作符 if (a > 0 && b > 0) { // 正确:实际上就是 (a > 0) && (b > 0) printf("a和b都为正数\n"); } return 0; }

第十二部分:常见关键字说明

一、auto

// auto 用于自动类型推导(C++11),C语言中很少使用 auto int a = 10; // 等价于 int a = 10(默认就是auto)

二、static

#include <stdio.h> // 1. 修饰局部变量:延长生命周期至整个程序 void test_static() { static int a = 1; // 只初始化一次 a++; printf("%d\n", a); } // 2. 修饰全局变量:限制作用域为本文件 // static int global_var = 100; // 其他文件无法访问 // 3. 修饰函数:限制作用域为本文件 static void helper() { // 只能在本文件内调用 } int main() { test_static(); // 2 test_static(); // 3 test_static(); // 4 return 0; }

三、const

int main() { const int a = 10; // a是只读变量,不能修改 // a = 20; // 错误! // const修饰指针 int x = 10, y = 20; const int* p1 = &x; // 指向常量的指针:不能通过p1修改指向的值 int* const p2 = &x; // 常量指针:p2的指向不能改变 const int* const p3 = &x; // 既不能修改值,也不能修改指向 p1 = &y; // 可以修改指向 *p2 = 30; // 可以修改值 // p2 = &y; // 错误! return 0; }

四、extern

// file1.c int global_var = 100; // file2.c extern int global_var; // 声明外部变量 int main() { printf("%d\n", global_var); // 100 return 0; }

五、volatile

// volatile 告诉编译器不要优化这个变量,每次直接从内存中读取 volatile int flag = 1; // 常用于多线程编程或硬件寄存器访问

六、register

// register 建议编译器将变量存储在寄存器中(现代编译器自动优化,很少使用) register int counter = 0; // 不能对register变量取地址 // int* p = &counter; // 错误!

第十三部分:枚举常量(enum)

#include <stdio.h> enum Color { RED, // 默认0 GREEN, // 1 BLUE // 2 }; enum Weekday { MON = 1, TUE, // 2 WED, // 3 THU, // 4 FRI, // 5 SAT, // 6 SUN // 7 }; int main() { enum Color c = RED; printf("RED = %d\n", RED); // 0 printf("GREEN = %d\n", GREEN); // 1 printf("BLUE = %d\n", BLUE); // 2 printf("MON = %d\n", MON); // 1 printf("SUN = %d\n", SUN); // 7 return 0; }

第十四部分:union(共用体)

一、基本用法

#include <stdio.h> union Data { int i; char c; float f; }; int main() { union Data d; printf("union大小:%zu\n", sizeof(d)); // 4(最大成员的大小) d.i = 65; printf("d.i = %d\n", d.i); // 65 printf("d.c = %c\n", d.c); // 'A' d.f = 3.14; printf("d.f = %f\n", d.f); // 3.14 return 0; }

二、判断大小端(经典面试题)

#include <stdio.h> // 方法1:使用union union Endian { int a; char b; }; int isLittleEndian1() { union Endian e; e.a = 1; return e.b == 1; // 小端返回1,大端返回0 } // 方法2:使用指针 int isLittleEndian2() { int a = 1; char* p = (char*)&a; return *p == 1; // 小端返回1,大端返回0 } int main() { if (isLittleEndian1()) { printf("小端模式\n"); } else { printf("大端模式\n"); } return 0; }

总结

一、操作符优先级速记

  1. 括号()

  2. 单目++--!~*&sizeof

  3. 算术*/%+-

  4. 移位<<>>

  5. 关系<<=>>===!=

  6. 位运算&^|

  7. 逻辑&&||

  8. 条件?:

  9. 赋值=+=-=

  10. 逗号,

二、常见陷阱总结

陷阱说明
===混淆if (a = 10)是赋值,永远为真
短路求值&&||会短路,后面的表达式可能不执行
整数除法截断10 / 3 = 3,不是3.33
移位位数过大移位位数 ≥ 类型位数是未定义行为
优先级错误a & b == 0实际是a & (b == 0)

操作符是C语言的基本构件,掌握它们的使用规则和优先级是写出正确代码的基础。

学习建议:

  1. 不确定优先级时使用括号明确顺序

  2. 理解短路求值对代码执行的影响

  3. 注意整数除法、取模的运算规则

  4. 熟悉位操作在嵌入式、加密、标志位等场景的应用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 16:14:36

揭秘输出反灌电流ZVS反激:低成本实现软开关的工程实践

1. 低成本ZVS反激变换器的核心优势 我第一次接触这种利用输出反灌电流实现ZVS的反激变换器时&#xff0c;最惊讶的就是它的电路结构竟然如此简单。相比常见的有源箝位方案&#xff0c;它省去了额外的开关管和驱动电路&#xff0c;整个拓扑看起来就像普通反激变换器加了个同步整…

作者头像 李华
网站建设 2026/4/25 16:09:59

深入浅出聊聊“合并报表”这一概念

理解“合并报表”这个概念&#xff0c;需要绕开字面上“合并”二字带来的简单加总的想象。它的核心&#xff0c;并不在于“合”&#xff0c;而在于“纠偏”&#xff0c;纠正因法律形式分割而扭曲的经济实质。本质上&#xff0c;合并报表是一套为了特定目的而创设的会计呈现方式…

作者头像 李华
网站建设 2026/4/25 16:09:36

Diablo Edit2:暗黑破坏神2角色存档编辑器的完整指南

Diablo Edit2&#xff1a;暗黑破坏神2角色存档编辑器的完整指南 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit 你是否厌倦了在暗黑破坏神2中反复刷怪升级&#xff1f;是否想要快速测试不同的职业…

作者头像 李华
网站建设 2026/4/25 16:09:21

Windows新系统_安装anaconda-2026-4.24

一、Anaconda官网下载 现在这个官网需要注册成功后才可以下载 1、找到自己系统适合的版本下载 二、开始安装 有点问题先看其他的的&#xff0c;后面会解决更新上来 这里安装版本示例 Miniconda3-latest-Windows-x86_64.exe 1、点击next 2、点击 agree 3、点击next 4、…

作者头像 李华
网站建设 2026/4/25 16:05:58

Qwen-Agent智能体框架实战:从工具调用到多智能体协作

1. 项目概述&#xff1a;从大语言模型到智能体框架的跃迁最近在折腾大语言模型应用落地的朋友们&#xff0c;估计都绕不开一个词——“智能体”。当大家还在为如何让一个LLM&#xff08;大语言模型&#xff09;稳定输出JSON格式而头疼时&#xff0c;业界已经悄然进入了下一个阶…

作者头像 李华