C++运算符包括算术、逻辑、位运算等,用于执行计算和操作。算术运算符处理基本数学运算,注意整数除法截断和自增/自减前置后置区别;逻辑运算符支持短路求值,常用于条件判断;位运算符操作二进制位,适用于底层优化。运算符优先级和结合性决定表达式求值顺序,建议用括号明确意图。常见陷阱有整数除法、短路副作用、有符号数位移和赋值与比较混淆,可通过类型转换、避免复杂表达式和编译器警告规避。高级用法包括sizeof获取大小、三元运算符简化条件赋值、逗号运算符串联操作,以及运算符重载实现自定义类型自然操作,提升代码可读性和表达力。

C++的运算符种类繁多,它们是语言执行各种计算和操作的基石。简单来说,它们就是我们用来告诉程序“做点什么”的符号。从最常见的加减乘除,到复杂的位级操作,它们赋予了代码生命力。今天我们主要聊聊算术、逻辑和位运算这几类,它们在日常编程里出镜率极高。
解决方案
C++的运算符,就像是程序世界的动词,它们指引着数据如何被处理、被转换。
算术运算符:这没什么好说的,小学数学就学了。加法
+、减法
-、乘法
*、除法
/,还有取模
%。不过,这里有个小坑,尤其是在处理整数除法时。比如
5 / 2,结果是
2,不是
2.5,因为整数除法会直接截断小数部分。这有时候会让人猝不及防,尤其是在没有意识到类型转换的时候。另外,自增
++和自减
--也属于算术运算符。它们既可以前置(
++i),也可以后置(
i++)。前置是先改变值再使用,后置是先使用值再改变。这个区别在表达式里用起来,效果可大不一样,稍不留神就可能出bug。
int a = 10, b = 3; int sum = a + b; // 13 int diff = a - b; // 7 int product = a * b; // 30 int quotient = a / b; // 3 (整数除法) int remainder = a % b; // 1 int x = 5; int y = ++x; // x变为6,y为6 int z = x++; // x变为7,z为6
逻辑运算符:这玩意儿主要是用来做条件判断的,在控制流语句(
if,
while等)里简直是核心。逻辑与
&&、逻辑或
||、逻辑非
!。
&&要求两边都为真结果才为真;
||只要一边为真结果就为真;
!则是取反。有个很重要的特性叫“短路求值”:对于
&&,如果左边已经是假了,右边就不会再计算了;对于
||,如果左边已经是真了,右边也不会再计算。这个特性有时候能帮我们避免一些空指针解引用或者其他潜在的运行时错误,但有时也可能导致一些你期望执行的副作用函数没有被调用,得留心。
立即学习“C++免费学习笔记(深入)”;
bool isSunny = true;
bool isWarm = false;
if (isSunny && isWarm) { // false
// 不会执行
}
if (isSunny || isWarm) { // true
// 会执行
}
if (!isWarm) { // true
// 会执行
}位运算符:这些操作符是直接作用在整数的二进制位上的。对于我个人而言,这块儿一开始是有点抽象的,但一旦理解了,你会发现它们在处理底层数据、优化性能(比如某些算法)时非常强大。位与
&、位或
|、位异或
^、位非
~(取反)、左移
<<、右移
>>。位与常用来清零特定位或检查某位是否为1;位或常用来设置特定位为1;位异或则可以用来翻转特定位,或者在加密、数据交换中派上用场。移位操作则常用来快速乘以或除以2的幂,或者高效地提取/插入位。注意,右移对于有符号数,行为可能因编译器而异(算术右移或逻辑右移),但对于无符号数,通常是逻辑右移,高位补0。
unsigned int a = 5; // 二进制 0101 unsigned int b = 3; // 二进制 0011 unsigned int resultAnd = a & b; // 0001 (1) unsigned int resultOr = a | b; // 0111 (7) unsigned int resultXor = a ^ b; // 0110 (6) unsigned int resultNot = ~a; // 1111...1010 (取决于int大小,通常是一个很大的负数或无符号数) unsigned int shiftedLeft = a << 1; // 1010 (10) unsigned int shiftedRight = a >> 1; // 0010 (2)
C++运算符的优先级和结合性如何影响表达式求值?
这就像是数学里的“先乘除后加减”,运算符也有自己的“地位”和“方向”。优先级决定了在没有括号的情况下,哪个运算符先执行。比如,乘法和除法的优先级就高于加法和减法。如果一个表达式里有多个相同优先级的运算符,那就看它们的结合性了。大多数二元运算符(比如加减乘除)都是左结合的,这意味着它们从左到右依次计算。而赋值运算符(
=)是右结合的,
a = b = c会先计算
b = c,然后把结果赋给
a。
理解这些规则至关重要,否则你写出来的代码可能和你想的完全不是一回事。我个人经验是,与其死记硬背那张长长的优先级表,不如记住几个关键点:算术运算符优先级最高(乘除高于加减),然后是位运算符,接着是关系运算符,最后是逻辑运算符,赋值运算符最低。当你不确定或者表达式比较复杂时,最简单也最保险的方法就是:加括号。括号可以强制改变运算顺序,让你的意图清晰明了,也能避免很多潜在的bug。代码的可读性,有时候比所谓的“简洁”更重要。
使用C++运算符时常见的陷阱有哪些?如何避免?
踩坑是学习的必经之路,运算符也不例外。我见过不少,自己也踩过不少。
一个很经典的坑就是整数除法。前面提过,
5 / 2是
2。如果你期望得到
2.5,但又忘记了类型转换,那结果肯定不对。解决方案很简单,把其中一个操作数或者两个都转换为浮点类型,比如
static_cast。(5) / 2
逻辑运算符的短路求值。这既是优点也是陷阱。如果你在
&&或
||的右侧表达式中放置了有副作用的代码(比如函数调用,或者
++操作),并且期望它总是执行,那短路求值可能会让你失望。例如,
if (ptr != nullptr && ptr->doSomething()),如果
ptr是空指针,
doSomething()就不会被调用。大多数时候这是好事,但如果你希望
doSomething()总是执行,你就得把它移到条件判断之外。
位运算符与有符号数。对有符号数使用位运算符,尤其是右移,结果可能不如你所愿。因为符号位的原因,右移时最高位补0还是补1,取决于具体实现(算术右移还是逻辑右移)。为了避免这种不确定性,通常建议对无符号类型进行位操作。
前置++
与后置++
。
int i = 0; int j = i++;和
int k = ++i;的结果是不同的。前者
j会是
0,
i变成
1;后者
k会是
1,
i也变成
1。在复杂的表达式中,这种细微差别可能导致难以追踪的错误。我的建议是,如果不是为了追求极致的性能(编译器通常会优化掉大部分差异),或者明确需要其副作用,尽量独立使用
++和
--,不要把它们嵌套在复杂的表达式里。
赋值运算符与比较运算符的混淆。这是个老生常谈的问题了,尤其是在C/C++里:
if (a = b)而不是
if (a == b)。前者是将
b的值赋给
a,然后判断
a的值(非零为真),这通常不是你想要的。现代编译器通常会对此发出警告,但最好还是养成习惯,避免这种低级错误。
C++中除了基础运算符,还有哪些值得关注的高级用法或概念?
除了那些我们每天都在用的算术、逻辑和位运算符,C++还提供了一些特殊或者说“高级”的运算符,以及一个非常重要的概念——运算符重载。
首先,sizeof
运算符。它不是一个函数,而是一个编译时运算符,用来获取一个类型或变量在内存中占用的字节数。这在内存管理、数据结构对齐、或者序列化数据时非常有用。比如
sizeof(int)会告诉你一个整数占多少字节,
sizeof(myStruct)则会告诉你自定义结构体的大小。它能让你对内存布局有更直观的认识。
然后是条件运算符(三元运算符)?:
。这玩意儿简直是
if-else语句的精简版。
条件 ? 表达式1 : 表达式2。如果条件为真,结果是表达式1的值;否则是表达式2的值。它能让一些简单的条件赋值语句变得非常紧凑。虽然用多了可能会让代码看起来有点密集,但合理使用能提高简洁性。
int x = 10; int y = (x > 5) ? 20 : 30; // y为20
再来是逗号运算符,
。这个就比较少见了,也容易被误解。它允许你在一个表达式中执行多个操作,并返回最后一个表达式的值。比如
int i = (a++, b++);,
i会得到
b自增前的值,但
a和
b都会自增。在某些宏定义或者循环的初始化/步进部分可能会见到它,但日常代码里,为了清晰,我个人倾向于避免它的过度使用。
最后,也是C++独有的强大特性:运算符重载(Operator Overloading)。这允许你为自定义类型(比如你定义的类)重新定义现有运算符的行为。比如,你可以让两个
MyVector对象直接使用
+运算符进行向量相加,而不是写一个
add()方法。这极大地提高了代码的可读性和直观性,让你的自定义类型能够像内置类型一样自然地参与运算。当然,滥用重载会导致代码难以理解和维护,所以,重载运算符时要遵循“最小惊讶原则”,让它的行为符合用户的直觉。这是C++面向对象设计中一个非常核心且强大的工具,也是它比C语言更“高级”的一个体现。
这些运算符和概念,从底层内存操作到高级的类型行为定制,共同构成了C++强大而灵活的表达能力。掌握它们,你就能更深入地理解代码的运行机制,写出更高效、更优雅的程序。










