C/C++条件(三目)运算符巧妙原理解析

本文解析了一段复杂的汇编代码,并将其转化为简洁的C++代码。通过对setg指令及后续指令的深入分析,揭示了编译器如何巧妙地利用汇编指令实现高效的条件判断和赋值操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近一直研究一个对个人而言很有价值的一个LIB库的逆向。在今天下班后突然灵感闪现,这个断断续续逆了接近一周的核心管理类。终于在今天给逆完了。在最后一个函数里,碰到了之前基本没有用过的一条指令。(呵呵,高手见笑了!)当然光看单句的汇编指令,是没有办法看出具体的作用的,而且还很可能会认为原作者本来就是用汇编来实现的!呵呵,先不废话,先贴出反汇编代码一睹为快:

movdword ptr [b],64h // int b
xor eax,eax
cmpdword ptr [b],0
setg al
sub eax,1
and eax,64h
add eax,0C8h
mov dword ptr [a],eax // int a

今天的LIB里面的那段迷惑的代码就跟这段代码一致,唯独a、b变量不一样。当然这个不影响结果。一开始可能会对setg这条指令的用途不了解。二是看下面蓝色的三条指令,什么又是减,又是and,又是add一些莫名奇妙的立即数。还真让人迷惑这段代码翻译成C++将怎么写。难道就一句一句的翻译?这样的话恐怕一条汇编就是一句C++。而且到了setg这条指令时还真不知道怎么单独的将其翻译成C++的什么语句。呵呵!这可能也就是逆向所带来的乐趣之一吧(个人观点)!

好了。不废话,先分析下。首先b是一个变量,首先被赋值成0x64(100)。然后将b与0进行比较,如果有心的朋友会觉得奇怪,这个cmp的下面一条语句怎么不是跳转语句,一般都是比较后,然后根据比较结果进行跳转。否则cmp有什么意义呢?到这里的话误导我们的就是setg这条指令了。要了解它,首先得知道cmp会影响到标志寄存器的标志位。cmp是执行的减法操作,将前面的操作数减去后面的操作数。与sub的区别就是它不将减后的值放到目的操作数中。所以cmp有可能减溢出等,从而影响到了标志位。由此一来我们就算猜测都能知道setg应该与标志位有关系。然后通过资料或者奔腾X86指令集查找表(我使用的平台是INTEL X86 CPU)。就可以知道setg指令乃是大于零为真,setg al 就是如果cmp比较后大于零,al里的值将是0x01。setg的判断表达式就是(ZF=0 and SF=OF),还有个setle( ZF = 1 or SF <> OF ),等等还有几个,这里就不一一说明。有兴趣的朋友可以去查阅。

之后蓝色的三条指令,仔细分析会发现,如果eax为0的话,sub eax, 1后eax将是0xffffffff。之后再and eax, 64h结果eax为64h,之后再是加C8h。之后就给a变量了。然后再分析另外一种情况,假如eax为1, sub eax, 1后将是0,再and等于没运算,之后才是加上C8h,后面就一样了。前面的xor eax, eax就不说了,就是把eax清零。这样一分析,可以有个大概的C++语句的锥形。那就是三目运算符:(?:)。

好了,这句C++语句很短,那就是:

int b = 100;

int a = ( b <= 0) ? 300 : 200;

哈哈,很简单吧!其实逆向就是这样,分析一大段很可能翻译过来的C++代码就一句而已。这篇文章说是巧妙原理解析。这里的巧妙之处就在于编译器很聪明(MS很厉害),这些细节的技巧可以给我们很多的启发。这个三目运算在汇编层面上,首先编译器会把后面冒号两边的数字求差,差将用于and运算。b <= 100后eax要么为1要么为0。因此后面的sub eax, 1后eax要么为0xffffffff,要么为0,为0xffffffff表示b小于等于0。b小于等于0之后的and eax, 64h也就将300 - 200的差100赋值给了eax,之后再add C8h(200),便得到了300。反之,sub eax, 1后。 eax将为0。and运算后等于就没有算上差值,之后加上冒号后面的数。就是小的200了。呵呵!这些细节,MS的程序很细心啊!

还有就是一点,这里记录的差值是有符号的,而且是固定的冒号前面的数减去后面的数。如果前面的数小于后面的数,那么这里记录的将是一个负值,原理一样。

再举一个例子吧:

mov dword ptr [b],1
xor eax,eax
cmp dword ptr [b],0
setg al
lea eax,[eax+eax-1]
mov dword ptr [a],eax

这个例子就不用说了,直接贴C++代码:

int b = 1;
int a = ( b > 0 )? 1 : -1;

或者是BOOL类型的。注意看红色指令的巧用,它直接代替了and和add指令。大家慢慢体会!

如果后面冒号两边任意一边是变量的话,就不会被编译成这样子了,就会被编译成普通的跳转类似于if语句了。

好了,这里就是一点小小的心得,当是长个记性。高手大牛略过哈! - - 准备一会儿睡觉了!

<think>好的,我现在要解决用户的问题:“C语言中三目运算符是否属于条件运算符?”首先,我需要确认用户的问题核心在哪里。用户可能在学习C语言时遇到了三目运算符,但不确定它的分类,特别是是否属于条件运算符。需要明确三目运算符条件运算符之间的关系。 根据提供的引用资料,引用[1]直接提到“c语言三目运算符条件运算符)”,这表明三目运算符就是条件运算符。引用[5]详细解释了三目运算符的执行过程,即根据条件选择执行表达式2或表达式3,这也符合条件运算符的定义。 接下来,我需要确认C语言中是否有其他条件运算符。通常,条件控制结构如if-else是语句而非运算符。而三目运算符是C语言中唯一的三元运算符,其功能类似于条件语句,但以表达式的形式存在,因此被归类为条件运算符。 然后,结合引用中的信息,引用[3]和[4]展示了三目运算符的具体用法,进一步支持它作为条件运算符的身份。例如,引用[4]中的代码示例使用了条件表达式来选择不同的字符串输出,这正是条件运算符的典型应用场景。 最后,总结所有引用内容,可以明确回答用户:是的,C语言中的三目运算符属于条件运算符,并且是唯一的条件运算符,因为它根据条件判断选择不同的结果。需要确保回答结构清晰,引用相关文献,并生成相关问题以帮助用户深入理解。</think>### C语言中三目运算符条件运算符的关系 **结论**:**C语言中的三目运算符条件运算符的唯一实现形式**,两者是同一概念的不同表述[^1][^5]。 #### 定义解析 1. **条件运算符** 条件运算符是C语言中唯一的三元运算符(需要三个操作数),其语法为: $$ \text{条件表达式} \ ? \ \text{表达式1} \ : \ \text{表达式2} $$ 它根据条件表达式的布尔值选择执行表达式1或表达式2,并返回对应结果。 2. **三目运算符的别名** “三目运算符”这一名称来源于其需要三个操作数(条件、表达式1、表达式2),而“条件运算符”则强调其功能是根据条件进行分支选择。两者在C语言中完全等价[^2]。 #### 功能特性 - **执行逻辑**: 先计算条件表达式: - 若结果为真(非0),执行表达式1并返回其值; - 若结果为假(0),执行表达式2并返回其值。 示例: ```c int c = (a > b) ? a : b; // 若a > b,c = a;否则c = b[^5] ``` - **与if-else的区别** 三目运算符是表达式(有返回值),而if-else是语句(无返回值)。因此,三目运算符可直接用于赋值或参与其他运算[^2]。 #### 应用场景 - **简化条件赋值**: ```c const char* result = (score >= 60) ? "及格" : "不及格"; ``` - **嵌套使用**(需谨慎,避免可读性下降): ```c int max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值