一、问题解剖
交换二进制奇偶位,划分为两个步骤:
1、偶数位数据右移;
2、奇数位数据左移;
获取奇偶位数据
例:
int a = 23;
二进制表示为:(对奇偶位分别标色)
交换后为:
偶数位
采取二进制中偶数位全为1的数据,利用按位与&,便可获得其偶数位数据:
奇数位
采取二进制中奇数位全为1的数据,同理可得奇数位数据:
数据移位
偶数位左移1位便会变成奇数位:
同理,奇数位右移1位便移到偶数位:
数据合成
将奇偶位数据依照按位或 | 的规则合成,便得交换后的数据:
二、代码实现
获取偶数位数据,将a与0xaaaaaaaa按位与&便得,随后右移1位;获取奇数位数据,将a与0x55555555按位与&得,随后左移1位;再将奇偶位得数据按位或,便可得;
具体实现:
#define SWAPB(x) ((x & 0xaaaaaaaa)>>1)|((x & 0x55555555)<<1)
int main()
{
int a = 23;
int b = SWAPB(a);
printf("a = %d b = %d\n", a, b);
return 0;
}
测试得:
测试负数 -1:
由于整型在内存中以补码存储,而负数得原码与补码不同,先计算补码:
-1在内存中存储时,每一个bit位上都为1,都已交换二进制奇偶位时,应不改变结果,测试如下:
三、使用#define应注意
多用空格,提高代码可读性?
使用#define时,大可不必如此。
定义宏时,方式如下:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在,参数列表就都会被解释为stuff的一部分;
加上分号,代码更严谨?
与之相反,使用#define时,加上分号,易使代码出现难以察觉的错误,如下面一段代码:
#define MAX 100;
int main()
{
int a = 0;
if (1)
a = MAX;
else
a = 1;
}
一眼看过去貌似合理,但编译器此时便会报错,而且提示更让人摸不着头脑;
明明语句都正常在,为什么提醒要加一个语句呢?
原因便是,使用#define时,编译器会在预编译阶段对#define定义的符号进行替换,我们打开编译的文件,比对发现, a = MAX
被替换成了a = 100;;
,从而导致else语句失去了开始的if,缺乏语法结构而报错;
因此在使用#define时,切勿随便使用分号 ; 。
定义宏,代码越精简越好?
代码的艺术,越精简越优雅,但定义宏时的优雅,极有可能导致代码正确性的损失,如下:
#define ADD(x, y) x+y
int main()
{
int a = 2;
int b = 3;
int c = 4;
printf("%d ", c * ADD(a, b));
}
我们定义加法宏,a = 2, b = 3, c= 4, 2+3=5,4×5 = 20,结果应当输出20,真的如此吗?
我们看到输出了11,还是因为#define定义时,并不会先计算再传值,而是整体替换,在预编译时c * ADD(a, b)
会被替换成c * a + b
,4×2+3,结果自然是11,那我们添加括号呢?
#define ADD(x, y) (x+y)
#define SQUARE(x, y) (x * y)
int main()
{
int a = 2;
int b = 3;
int c = 4;
printf("%d\n", c * ADD(a, b));
printf("%d\n", SQUARE(a + 1, b));
}
ADD的问题解决了,增加括号便好,那以同样的方式定义乘方SQUARE
,同时增加一行乘方的测试函数,a = 2 , 2+1 = 3, 3×3 = 9,那么会不会输出9呢?
结果不然,这是因为SQUARE(a + 1, b)
会被替换成a + 1 * b
,于是输出5。
所以,定义数值计算的宏时,对变量与每一次计算,都需添加括号,避免由于参数中的操作符或邻近操作符之间优先级的不同,出现不可预料的相互作用。如下:
不可出现递归
如上解读,在预编译阶段,#define定义的字符被替换后,可以观察到其对应的语句会被删除,若采取递归,则会导致第二次使用时,其语句不能被编译器识别。
标签:奇偶,奇数,二进制,整数,偶数,int,ADD,define From: https://blog.51cto.com/u_15423682/6145625