取pi的前8位的解压密码_两种方式实现取16位变量的高低8位, 不严谨对比

程序如下,第一种方式是强制指针转换,再取结构体成员;第二种方式是简单的移位。前面这种写法得考虑大小端序, 后者不用管.

#include <stdio.h>

#define hi8(x)    ((( struct { unsigned char l; unsigned char h; } *)(&x))->h) 
#define lo8(x)    ((( struct { unsigned char l; unsigned char h; } *)(&x))->l) 
#define hi8a(x)    (((x) >> 8) & 0xff)
#define lo8a(x)    ((x) & 0xff)

int main(void)
{
    unsigned short x;
    scanf("%4x", &x);                     // 不能用字面量测试, 否则-Os编译时会直接给求值了
    printf("%2X %2Xn", hi8(x), lo8(x));
    printf("%2X %2Xn", hi8a(x), lo8a(x));
    return 0;
}

分别用mingw-gcc, arm-none-eabi-gcc, -O0和-Os选项编译,得到的汇编如下(只取scanf到第二次printf之间的部分)

mingw-gcc, -O0, 前者9条指令, 后者10条指令. 有点呆萌是吧...

	call	_scanf
	lea	eax, [esp+30]
	movzx	eax, BYTE PTR [eax]
	movzx	edx, al
	lea	eax, [esp+30]
	movzx	eax, BYTE PTR [eax+1]                   ; 这里错开1字节取值
	movzx	eax, al
	mov	DWORD PTR [esp+8], edx
	mov	DWORD PTR [esp+4], eax
	mov	DWORD PTR [esp], OFFSET FLAT:LC1      ; LC1是printf的格式字符串
	call	_printf
	movzx	eax, WORD PTR [esp+30]
	movzx	eax, ax
	movzx	edx, al
	movzx	eax, WORD PTR [esp+30]
	shr	ax, 8                      ; 移位
	movzx	eax, ax
	movzx	eax, al
	mov	DWORD PTR [esp+8], edx
	mov	DWORD PTR [esp+4], eax
	mov	DWORD PTR [esp], OFFSET FLAT:LC1
	call	_printf

mingw-gcc, -Os, 前者5条指令, 后者6条指令.

	call	_scanf
	movzx	eax, BYTE PTR [esp+30]
	mov	DWORD PTR [esp], OFFSET FLAT:LC1   ; 常量入栈提到前面了
	mov	DWORD PTR [esp+8], eax
	movzx	eax, BYTE PTR [esp+31]               ; 取值错开1字节
	mov	DWORD PTR [esp+4], eax
	call	_printf
	mov	ax, WORD PTR [esp+30]
	mov	DWORD PTR [esp], OFFSET FLAT:LC1
	movzx	edx, al
	movzx	eax, ah                          ; 这次把移位优化掉了, 直接利用ax寄存器的高低位.
	mov	DWORD PTR [esp+8], edx
	mov	DWORD PTR [esp+4], eax
	call	_printf

arm-none-eabi-gcc, -O0, 前者8条指令, 后者10条指令:

	bl	scanf
	sub	r3, fp, #6
	ldrb	r3, [r3, #1]	@ zero_extendqisi2
	mov	r2, r3
	sub	r3, fp, #6
	ldrb	r3, [r3]	@ zero_extendqisi2           ; 错开1字节
	ldr	r0, .L3+4                     ; .L3+4是printf的格式字符串
	mov	r1, r2
	mov	r2, r3
	bl	printf
	ldrh	r3, [fp, #-6]
	mov	r3, r3, lsr #8
	mov	r3, r3, asl #16
	mov	r3, r3, lsr #16
	and	r2, r3, #255
	ldrh	r3, [fp, #-6]
	and	r3, r3, #255
	ldr	r0, .L3+4
	mov	r1, r2
	mov	r2, r3
	bl	printf

arm-none-eabi-gcc, -Os, 这个清爽多了, 前者3条指令, 后者4条指令.

	bl	scanf
	ldrb	r1, [sp, #7]	@ zero_extendqisi2
	ldrb	r2, [sp, #6]	@ zero_extendqisi2    ; 错开1字节
	mov	r0, r4                   ; .L3+4被提前放进r4了
	bl	printf
	ldrh	r1, [sp, #6]
	mov	r0, r4
	mov	r1, r1, lsr #8             ; 移位
	ldrb	r2, [sp, #6]	@ zero_extendqisi2
	bl	printf

avr-gcc呢, 太长就不贴了. 不过-Os时反倒是移位方式的指令更少.

总之以上四种情况都是前者好些. 不过实际运行时也不一定, 没准后者更快呢?

----------------2020.2.22补充-------------------

还有一个重要的区别:前者可以作为左值,后者不行。

如下程序:

#include <stdio.h>

#define hi8(x)    ((( struct { unsigned char l; unsigned char h; } *)(&x))->h) 
#define lo8(x)    ((( struct { unsigned char l; unsigned char h; } *)(&x))->l) 

int main(void)
{
    unsigned short x = 0x1234;

    hi8(x)--;
    lo8(x)++;

    printf("%xn", x);

    return 0; 
}

运行,结果是1135。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值