位运算(&、|、^、~、>>、<<)

本文详细介绍了计算机中的位运算,包括按位与、按位或、异或、取反、左移和右移等运算符的基本概念及应用。通过具体实例展示了如何利用位运算提升代码执行效率。

1.位运算概述

从现代计算机中所有的数据二进制的形式存储在设备中。即0、1两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算,即将符号位共同参与运算的运算。

口说无凭,举一个简单的例子来看下CPU是如何进行计算的,比如这行代码:

int a = 35;
int b = 47;
int c = a + b;

计算两个数的和,因为在计算机中都是以二进制来进行运算,所以上面我们所给的int变量会在机器内部先转换为二进制在进行相加:

35:  0 0 1 0 0 0 1 1
47:  0 0 1 0 1 1 1 1
————————————————————
82:  0 1 0 1 0 0 1 0

所以,相比在代码中直接使用(+、-、*、/)运算符,合理的运用位运算更能显著提高代码在机器上的执行效率。

 

2.位运算概览

符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
^异或两个位相同为0,相异为1
~取反0变1,1变0
<<左移各二进位全部左移若干位,高位丢弃,低位补0
>>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

 

3.按位与运算符(&)#

定义:参加运算的两个数据,按二进制位进行“与”运算。

运算规则:

0&0=0  0&1=0  1&0=0  1&1=1

总结:两位同时为1,结果才为1,否则结果为0

例如:3&5 即 0000 0011& 0000 0101 = 0000 0001,因此 3&5 的值得1。

注意:负数按补码形式参加按位与运算。

与运算的用途:

1)清零

如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

2)取一个数的指定位

比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

3)判断奇偶

只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

4.按位或运算符(|)

定义:参加运算的两个对象,按二进制位进行“或”运算。

运算规则:

0|0=0  0|1=1  1|0=1  1|1=1

总结:参加运算的两个对象只要有一个为1,其值为1。

例如:3|5即 0000 0011| 0000 0101 = 0000 0111,因此,3|5的值得7。 

注意:负数按补码形式参加按位或运算。

或运算的用途:

1)常用来对一个数据的某些位设置为1

比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

5.异或运算符(^)

定义:参加运算的两个数据,按二进制位进行“异或”运算。

运算规则:

0^0=0  0^1=1  1^0=1  1^1=0

总结:参加运算的两个对象,如果两个相应位相同为0,相异为1。

异或的几条性质:

1、交换律

2、结合律 (a^b)^c == a^(b^c)

3、对于任何数x,都有 x^x=0,x^0=x

4、自反性: a^b^b=a^0=a;

异或运算的用途:

1)翻转指定位

比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。

2)与0相异或值不变

例如:1010 1110 ^ 0000 0000 = 1010 1110

3)交换两个数

复制代码

void Swap(int &a, int &b){
    if (a != b){
        a ^= b;
        b ^= a;
        a ^= b;
    }
}

复制代码

6.取反运算符 (~)

定义:参加运算的一个数据,按二进制进行“取反”运算。
运算规则: 

~1=0
~0=1

总结:对一个二进制数按位取反,即将0变1,1变0。

异或运算的用途:

1)使一个数的最低位为零

使a的最低位为0,可以表示为:a & ~1。~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为“ ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

7.左移运算符(<<)

定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。

若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

8.右移运算符(>>)

定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。

操作数每右移一位,相当于该数除以2。

10.复合赋值运算符

位运算符与赋值运算符结合,组成新的复合赋值运算符,它们是:

&=        例:a&=b    相当于     a=a&b

|=        例:a|=b    相当于     a=a|b

>>=      例:a>>=b   相当于     a=a>>b

<<=      例:a<<=b     相当于      a=a<<b

^=        例:a^=b    相当于   a=a^b

运算规则:和前面讲的复合赋值运算符的运算规则相似。

不同长度的数据进行位运算:如果两个不同长度的数据进行位运算时,系统会将二者按右端对齐,然后进行位运算。

以“与运算”为例说明如下:我们知道在C语言中long型占4个字节,int型占2个字节,如果一个long型数据与一个int型数据进行“与运算“,右端对齐后,左边不足的位依下面三种情况补足,

1)如果整型数据为正数,左边补16个0。

2)如果整型数据为负数,左边补16个1。

3)如果整形数据为无符号数,左边也补16个0。

如:long a=123;int b=1;计算a& b。

如:long a=123;int b=-1;计算a& b。

如:long a=123;unsigned intb=1;计算a & b。

 

原文:https://www.cnblogs.com/yueshutong/p/11244534.html

&lt;think&gt; 我们被要求使用位运算和算术运算(! ~ &amp; ^ | + &lt;&lt; &gt;&gt;)来实现C语言的三目运算符功能。 三目运算符:condition ? expr1 : expr2 当condition为真(非0)时,返回expr1;否则返回expr2。 思路: 我们可以利用条件表达式的结果(0或非0)来构造一个掩码(mask),使得: 当条件为真时,掩码为全1(即二进制111...111,在补码中表示-1) 当条件为假时,掩码为全0(即二进制000...000,表示0) 然后,我们可以通过以下方式选择expr1或expr2: result = (mask &amp; expr1) | (~mask &amp; expr2); 解释: 当mask为全1时: (mask &amp; expr1) 得到 expr1 (~mask) 为全0,所以 (~mask &amp; expr2) 为0 所以结果为 expr1 当mask为全0时: (mask &amp; expr1) 为0 (~mask) 为全1,所以 (~mask &amp; expr2) 得到 expr2 所以结果为 expr2 关键是如何将条件condition转换为全0或全1的掩码。 我们可以利用逻辑非(!)和按位取反(~)来实现: condition为0:我们希望掩码为0。 condition非0:我们希望掩码为全1(即-1)。 步骤: 1. 将条件condition转换为0或1: flag = !condition; // 如果condition为0,则flag=1;非0则flag=0。 但实际上我们想要的是:条件为真时掩码为全1,条件为假时掩码为全0。 因此,我们可以先取非(!)得到相反的逻辑值,然后再调整。 2. 另一种思路是: mask = ~(!condition) + 1; 解释: 当condition非0时,!condition=0,则~0得到全1(即-1),然后+1?不对,这样会变成0。 这个思路不正确。 重新调整: 我们可以利用:-1的补码表示是全部位为1,而0就是全部位为0。 如何得到这个掩码?我们可以用以下方法: mask = -(!(!condition)); // 双重否定?这也不对。 更好的方法: 首先,我们知道: 如果condition非0,则!condition=0,再取非:!!condition=1。 如果condition为0,则!condition=1,再取非:!!condition=0。 但是我们想要的是: 条件为真(非0)时,mask=-1(全1);条件为假(0)时,mask=0。 我们可以利用:-1的补码表示是0xFFFFFFFF(假设32位),而0的补码是0x00000000。 如何将1变成-1?将0变成0? 我们可以将1进行扩展:1 -&gt; 0xFFFFFFFF,0-&gt;0x00000000。 具体操作: mask = - (condition != 0); // 但是这里用了!=,不符合要求(题目要求使用指定的运算符)。 题目要求只能使用! ~ &amp; ^ | + &lt;&lt; &gt;&gt;,所以不能使用!=。 因此,我们可以用以下步骤: Step1: 将condition转换为布尔值(0或1):flag = !!condition; Step2: 然后将flag扩展为全0或全1。如何扩展? 我们可以将flag取反加1得到掩码的相反数?或者利用算术右移。 扩展方法: 利用算术右移的符号扩展:假设我们有一个32位的整数,将flag左移31位,然后再算术右移31位。 具体: mask = (!!condition) &lt;&lt; 31; // 如果condition非0,则!!condition=1,左移31位得到0x80000000(最高位为1,其余为0) mask = mask &gt;&gt; 31; // 算术右移31位,由于是带符号的,所以高位用符号位(1)填充,得到0xFFFFFFFF(即-1) 如果condition为0,则!!condition=0,左移31位为0,右移31位还是0。 但是注意:这种方法依赖于整数是32位的,而且算术右移的行为。在C语言中,对于有符号整数,右移是算术右移(符号位填充),但对于无符号整数是逻辑右移(0填充)。因此,我们需要使用有符号整数。 但是题目没有说明整数类型,而且我们使用的运算符都是按位运算,所以我们需要一个通用的方法。 另一种方法: 我们利用0减去1会得到全1(在补码中,0-1=-1,其二进制表示是全部为1)的特性。 令 flag = !condition; // 条件为真(非0)时,flag=0;条件为假(0)时,flag=1。 然后,mask = (0 - flag); // 当flag=0时,0-0=0;当flag=1时,0-1=-1(全1)。 所以:mask = -flag; 因此,我们可以这样: mask = -(!condition); // 注意:这里!condition在条件为真时是0,为假时是1。 所以: 条件为真:mask = -0 = 0 条件为假:mask = -1 = 全1 但注意,我们想要的是:条件为真时掩码为全1,条件为假时掩码为0?不对,我们上面的公式要求: result = (mask &amp; expr1) | (~mask &amp; expr2); 当条件为真时,我们希望得到expr1,所以此时mask应该为全1;条件为假时,mask应该为全0。 所以这里我们得到的mask是反的。因此,我们调整一下: 我们让条件为真时mask为全1,条件为假时mask为全0,那么可以: mask = - (condition != 0) // 但我们不能使用!= 用上面的方法:我们可以用!!condition,然后mask = - (!!condition) ?不对,因为!!condition在条件为真时是1,条件为假时是0。 那么:mask = - (!!condition) 得到:条件为真时mask=-1,条件为假时mask=0。这正好是我们需要的。 所以: mask = - (!!condition); 但是,题目要求不能使用减法?注意,题目允许使用运算符:! ~ &amp; ^ | + &lt;&lt; &gt;&gt;,其中没有减法。 我们可以用取反加1来得到负数:-x = ~x + 1 所以:mask = ~(!!condition) + 1; 验证: 当condition为非0:!!condition=1,则~1+1 = ...11111110 + 1 = ...11111111 = -1(全1) 当condition为0:!!condition=0,则~0+1 = ...11111111 + 1 = ...00000000(溢出,得到0) 因此,我们可以这样: int mask = ~(!!condition) + 1; 然后,结果 = (mask &amp; expr1) | (~mask &amp; expr2); 但是,注意:expr1和expr2可以是任意整数,而按位与和或操作要求整数类型。如果expr1和expr2的类型不同,可能会有问题,但这里我们假设都是整数类型。 另外,注意:当condition为0时,!!condition=0,mask=0;当condition非0时,!!condition=1,mask=~1+1=0xFFFFFFFE+1? 不对,~1是0xFFFFFFFE(假设32位),然后+1得到0xFFFFFFFF(即-1)。 因此,我们可以用以下代码实现: int conditional(int condition, int expr1, int expr2) { int mask = ~(!!condition) + 1; return (mask &amp; expr1) | (~mask &amp; expr2); } 但是,我们也可以不用两次位运算,另一种常见的方式是: return (condition * expr1) + ((!condition) * expr2); 然而,题目要求使用指定的位运算符,而且乘法不在允许的运算符列表中(题目允许:! ~ &amp; ^ | + &lt;&lt; &gt;&gt;),所以乘法不能用。 因此,我们使用掩码的方法。 但是,上面的掩码方法中,我们使用了!!condition,而!!condition用了逻辑非(!),这是允许的。 注意:逻辑非(!)在C语言中,对于非0输入返回0,对于0输入返回1。这符合要求。 然而,题目要求使用指定的运算符(! ~ &amp; ^ | + &lt;&lt; &gt;&gt;),我们使用了!和+,以及后面的&amp; | ~,都是允许的。 但是,我们也可以不用!!,而只用一次!,然后利用算术运算得到掩码。我们上面已经用了两次!(即!!),但也可以只用一次: mask = -(!condition); // 但是这里我们想要的是条件为真时掩码为-1,条件为假时掩码为0,那么: 条件为真:!condition=0,则-0=0(但我们想要-1) -&gt; 所以不对。 mask = -(!!condition); // 条件为真:!!condition=1,则-1;条件为假:0,则-0=0。但是如何用位运算表示负号?我们用~x+1。 所以,我们最终用: mask = ~(!!condition) + 1; 因此,实现如下: int conditional(int condition, int expr1, int expr2) { // 将condition转换为0或1:使用两次逻辑非 int bool_flag = !!condition; // 计算掩码:如果bool_flag为1,则掩码为全1(-1);如果为0,则掩码为0。 int mask = ~bool_flag + 1; // 因为-1的补码是:~1+1=0xFFFFFFFE+1=0xFFFFFFFF,而-0=0。 // 注意:这里~bool_flag+1 等价于 -bool_flag(数学上),但使用位运算。 return (mask &amp; expr1) | (~mask &amp; expr2); } 但是,还有一种更简洁的方法,不需要两次逻辑非,只需要一次: int mask = ~(!condition) + 1; // 这样对吗? 当condition非0:!condition=0,则~0=全1,然后+1=全0(因为~0+1=0,在补码中,0就是0,但注意~0是-1,-1+1=0)-&gt; 得到0,但我们想要的是全1(-1)?不对。 所以不行。 因此,还是需要两次逻辑非。 另一种不使用!!的方法: 我们可以用:flag = (condition | -condition) &gt;&gt; (sizeof(int)*8-1); 这个操作可以将condition的符号位扩展到整个字。因为: 如果condition&gt;0,则-condition&lt;0,所以(condition | -condition)的最高位一定是1(因为负数的最高位为1),然后右移(算术右移)31位(假设32位)得到全1。 如果condition&lt;0,同样-condition&gt;0,但是condition本身最高位就是1,所以或操作后最高位也是1,右移后全1。 如果condition=0,则condition和-condition都是0,右移后全0。 但是,条件为非0时,我们想要全1,0时全0。而这里非0时(无论正负)都得到全1,0时全0。符合要求。 但是,这里使用了-(负号)和sizeof,而且负号可以用位运算实现吗?题目要求只能用指定的运算符,而负号不在其中,但我们可以用~x+1来表示-x。 所以: int sign = condition | ((~condition)+1); // 即 condition | (-condition) 这里用~condition+1表示-condition // 然后,算术右移(注意,为了可移植性,我们需要知道整数位数,但题目没有要求可移植,假设32位) int mask = sign &gt;&gt; 31; // 算术右移31位,将符号位扩展到所有位 但是,注意:当condition为最小的负数(比如0x80000000)时,~condition+1还是0x80000000(因为补码范围不对称),所以condition | (-condition) = 0x80000000 | 0x80000000 = 0x80000000,然后算术右移31位得到0xFFFFFFFF(即-1),符合要求。 因此,我们可以这样: int negative_condition = ~condition + 1; int sign = condition | negative_condition; int mask = sign &gt;&gt; 31; // 这里假设int是32位,所以右移31位 然后,结果 = (mask &amp; expr1) | (~mask &amp; expr2); 但是,这种方法依赖于整数位数(32位),而且需要算术右移(所以sign必须是有符号整数)。另外,它没有使用!运算符。 由于题目没有指定整数位数,而且要求使用指定的运算符(包括!),所以我们可以选择第一种方法(使用!!)更简单。 但是,题目要求使用运算符:! ~ &amp; ^ | + &lt;&lt; &gt;&gt;,并没有规定不能使用两次!,所以第一种方法可行。 然而,我们也可以只用一次!,然后利用按位运算来得到掩码: 我们想要:当condition非0时,mask全1;为0时,mask全0。 令 flag = !condition; // flag为0(当condition非0)或1(当condition为0) 那么,我们想要的mask其实就是当flag为0时全1,当flag为1时全0?不对,因为: condition非0 -&gt; flag=0 -&gt; 我们希望mask全1 condition为0 -&gt; flag=1 -&gt; 我们希望mask全0 所以,我们可以:mask = ~flag + 1; // 当flag=0时,~0+1=0xFFFFFFFF+1=0(因为~0是全1,加1溢出为0)?不对。 实际上,当flag=0时,我们想要全1:而~0+1=0,这不对。 再调整:我们想要的是:mask = -flag; // 当flag=0时,-0=0;当flag=1时,-1=全1。但是这里flag=0时我们想要全1,而flag=1时我们想要0?不对,正好反了。 所以,我们可以先取反:mask = -(!flag); // 但是!flag在condition非0时(flag=0)!flag=1,在condition为0时(flag=1)!flag=0。 那么:当condition非0:mask=-1(全1);当condition为0:mask=-0=0。这符合要求。 因此:mask = -(!flag) = -( ! (!condition) ) = -(!!condition) // 又回到了两次逻辑非。 所以,两次逻辑非是绕不开的。 结论:使用两次逻辑非的方法。 因此,我们实现如下: int conditional(int condition, int expr1, int expr2) { int mask = ~(!!condition) + 1; return (mask &amp; expr1) | (~mask &amp; expr2); } 但是,我们也可以不用两次逻辑非,而用一次逻辑非加其他运算: 令 flag = !condition; // 我们希望当flag=0时(condition非0)得到全1,flag=1时(condition为0)得到全0。 那么,我们可以:mask = ~flag + 1; // 当flag=0:~0+1=0;当flag=1:~1+1=0xFFFFFFFE+1=0xFFFFFFFF(即-1)?不对,我们想要的是:condition非0时mask全1,这里却得到了0;condition为0时mask全1?不对。 所以,我们想要的是:condition非0时mask为全1,condition为0时mask为0。 那么,我们可以:mask = ~flag + 1; // 当flag=0(condition非0)时,~0+1=0(但我们想要全1)-&gt; 失败。 或者,我们可以:mask = ~(flag-1); // 当flag=0(condition非0)时,flag-1=-1,~(-1)=0(全0,但我们想要全1)-&gt; 不对。 或者:mask = (flag-1); // 当flag=0时,-1(全1);当flag=1时,0(全0)。这符合要求! 但是,flag-1 可以用位运算表示:flag + (-1),而-1可以用~0表示(因为~0是全1,即-1),所以: mask = flag + (~0); // 当flag=0:0 + (-1) = -1(全1);当flag=1:1+(-1)=0(全0)。符合要求。 因此,我们可以: int mask = flag + (~0); // 即 flag - 1 所以,整个函数可以写为: int conditional(int condition, int expr1, int expr2) { int flag = !condition; // condition非0则flag=0;为0则flag=1。 int mask = flag + (~0); // 相当于 flag - 1 return (mask &amp; expr1) | (~mask &amp; expr2); } 验证: 当condition非0:flag=0,mask=0+(~0)=0xFFFFFFFF(全1,即-1) 则:mask &amp; expr1 = expr1,~mask = 0,所以~mask &amp; expr2 =0,结果=expr1。 当condition=0:flag=1,mask=1+(~0)=1+(-1)=0 则:mask &amp; expr1=0,~mask=全1,所以~mask &amp; expr2 = expr2,结果=expr2。 完美!而且只使用了一次逻辑非(!),以及允许的运算符。 但是,注意:~0 就是全1,也就是-1,所以可以用。而且加法是允许的。 因此,我们有两种方法: 方法1(两次逻辑非): mask = ~(!!condition) + 1; 方法2(一次逻辑非): mask = !condition + (~0); 两种方法都可以。我们选择方法2,因为它只用了一次逻辑非。 最终代码实现: int conditional(int condition, int expr1, int expr2) { int mask = !condition + (~0); // 注意:~0在补码中表示-1 return (mask &amp; expr1) | (~mask &amp; expr2); } 但是,注意:在C语言中,~0并不是在所有情况下都表示-1?在补码机器上,~0就是-1。而C语言标准要求整数使用补码表示(C99/C11标准中,负数使用补码),所以安全。 另外,我们也可以写成: int mask = !condition - 1; // 因为减1可以用加上-1(即~0)来代替,但题目不允许减法,所以用加法代替:mask = !condition + (~0) 因此,我们使用上述代码。 但是,还有一种更简洁的写法,不使用中间变量: return ( (!condition + (~0)) &amp; expr1 ) | ( ~(!condition + (~0)) &amp; expr2 ); 但这样可读性差,所以建议使用中间变量。 注意:以上方法都假设expr1和expr2是整型。如果它们是其他类型(比如指针、浮点数),则不能这样操作。但题目没有说明,我们假设整型。 此外,我们还可以使用异或来避免使用或运算,但题目允许使用|,所以没关系。 因此,我们给出最终答案。 注意:题目要求使用指定的运算符(! ~ &amp; ^ | + &lt;&lt; &gt;&gt;),我们使用了!、~&amp;、|、+,符合要求。 但是,我们没有使用^&lt;&lt;、&gt;&gt;,所以我们可以考虑用其他方法,但这不是必须的。 所以,我们就用上面的方法。 示例代码: #include &lt;stdio.h&gt; int conditional(int condition, int expr1, int expr2) { int mask = !condition + (~0); // 相当于 !condition - 1 return (mask &amp; expr1) | (~mask &amp; expr2); } int main() { int a = 10, b = 20; // 测试1:条件为真(非0) int c = conditional(a &lt; b, a, b); // 应该返回a(10) printf(&quot;%d\n&quot;, c); // 输出10 // 测试2:条件为假(0) c = conditional(a &gt; b, a, b); // 应该返回b(20) printf(&quot;%d\n&quot;, c); // 输出20 return 0; } 注意:在函数调用时,我们使用了a&lt;b和a&gt;b作为条件,它们会返回1或0,符合要求。 但是,我们的函数要求condition是整型,非0即真,0即假。 因此,这个函数可以工作。 总结:我们使用了一次逻辑非(!)和按位取反(~)以及加法(+)来构造掩码,然后使用按位与(&amp;)、或(|)和取反(~)来选择表达式。 注意:这里~0是一个全1的掩码,即-1。 所以,我们完成了任务。 &lt;/think&gt; 以下是使用位运算(`!`、`~`、`&amp;`、`^`、`|`、`+`、`&lt;&lt;`、`&gt;&gt;`)在 C 语言中模拟三目运算符 `condition ? expr1 : expr2` 的方法,完全遵循要求避免使用条件分支: ### 实现原理 1. **掩码生成**: 将条件 `condition` 转换为掩码 `mask`: - 若 `condition != 0`(真),则 `mask = 0xFFFFFFFF`(全 1) - 若 `condition == 0`(假),则 `mask = 0x00000000`(全 0) 通过 `!`(逻辑非)和 `~`(按位取反)实现: ```c int mask = !condition + (~0); // ~0 表示全 1(即 -1 的补码) ``` - **真**:`!condition = 0` &rarr; `0 + (-1) = -1`(全 1) - **假**:`!condition = 1` &rarr; `1 + (-1) = 0`(全 0) 2. **结果选择**: 利用掩码混合 `expr1` 和 `expr2`: ```c result = (mask &amp; expr1) | (~mask &amp; expr2); ``` - **真**:`mask` 全 1 &rarr; `expr1 | 0 = expr1` - **假**:`mask` 全 0 &rarr; `0 | expr2 = expr2` ### 完整代码示例 ```c #include &lt;stdio.h&gt; // 使用位运算实现三目运算符 int conditional(int condition, int expr1, int expr2) { int mask = !condition + (~0); // 生成掩码:真时为全1,假时为全0 return (mask &amp; expr1) | (~mask &amp; expr2); // 选择结果 } int main() { int a = 10, b = 20; // 测试1:条件为真 (a &lt; b),应返回 a int result1 = conditional(a &lt; b, a, b); // 等价于 a &lt; b ? a : b printf(&quot;Result1: %d\n&quot;, result1); // 输出 10 // 测试2:条件为假 (a &gt; b),应返回 b int result2 = conditional(a &gt; b, a, b); // 等价于 a &gt; b ? a : b printf(&quot;Result2: %d\n&quot;, result2); // 输出 20 // 测试3:条件为真(非0值),应返回 100 int result3 = conditional(5, 100, 200); // 等价于 5 ? 100 : 200 printf(&quot;Result3: %d\n&quot;, result3); // 输出 100 return 0; } ``` ### 关键步骤说明 1. **掩码计算**: - `!condition`:将任意非零值转换为 `0`,零值转换为 `1`。 - `~0`:生成全 1 的位模式(补码表示下的 `-1`)。 - `!condition + (~0)`: - 条件真 &rarr; `0 + (-1) = -1`(二进制 `111...111`) - 条件假 &rarr; `1 + (-1) = 0`(二进制 `000...000`) 2. **结果混合**: - `(mask &amp; expr1)`:条件真时保留 `expr1`,假时清零。 - `(~mask &amp; expr2)`:条件假时保留 `expr2`,真时清零。 - 通过位或 `|` 组合两部分结果。 ### 应用场景 - **嵌入式系统**:避免分支预测失败的开销[^1]。 - **加密算法**:实现常数时间的条件选择,防止时序攻击[^2]。 - **高性能计算**:消除分支,优化流水线执行[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值