第二章 位运算
在数字解码与编码的过程中,位运算的操作是司空见惯的事,同时位运算在提高程序的性能方面也独占鳌头,因此位运算操作是必需要深入了解的问题。
在乘法以及除法的操作中我可以使用未运行来提高代码的质量,例如:a = a * 16;这种操作完全可以替换为:a = a << 4;我们知道左移一位相当于将原数乘以2,左移N位则相当于乘以2^N,前提是在没有发生溢出的情况下;故上例即相当于将数a左移4位,对于某些乘以非2的整数幂情况,如 a = a * 9;则可以改写为a = (a << 3) + a; 同理右移相当于除以2的整数幂,当然以上所有情况都是在没有发生数据溢出的情况下,因此位运算操作要格外的小心,否则极有可能发生出错的情况。
在数据类型转换的过程中也需要做位运算操作,例如我们想将一个unsigned short类型的数据存入unsigned char类型的数组中,就需要进行位运算,首先分析知道unsigned short占用16个字节,unsigned char占用8个字节,想要将大字节的数据存入小字节,必须要对大字节进行分割,即将高8位与低8为分离开来分别存放,来看实现代码:
unsigned char * DrawCompo_Pj_BT_Change(unsigned short *subarray)
{
unsigned char temp[500];
(void)_sys_memset(&temp, 0x00, sizeof(temp) );
unsigned short i = 0;
while (subarray[i] != 0x0000)
{
if( (subarray[i] & 0xff00) == 0x0000)
{
temp[i++] = (unsigned char)(subarray[i] & 0x00ff);
}
else
{
temp[i] = (unsigned char)( (subarray[i] & 0xff00) >> 8);
temp[i++] = (unsigned char)(subarray[i] & 0x00ff);
}
}
temp[i] = ' ';
return temp;
}
temp[i] = (unsigned char)( (subarray[i] & 0xff00) >> 8);即取subarray[i]数据的高8位,temp[i++] = (unsigned char)(subarray[i] & 0x00ff);取低8位。这样就可以实现将高字节的数据完整的存入到低字节中。
位运算还可以用来判断变量的符号,我们知道对于一个有符号的变量,其最高位为其符号位,故检查改变的最高位即可知道该变量为正还是为负。看一段代码:
int main()
{
short test_data = -12;
if (test_data & 0xF000)
{
printf("This number is negative ");
}
else
{
printf("This number is positive ");
}
return 0;
}
对于想要交换两个数的值的时候,通常我们的做法如下:
void swap(int &data1, int &data2)
{
int temp = 0;
temp = data1;
data1 = data2;
data2 = temp;
}
这样的代码比较简单易懂,然而美中不足的是它会产生一个临时变量temp,接下来我们用位运算来重写这个程序;
void swap(int &data1, int &data2)
{
data1 = data1 ^ data2;
data2 = data1 ^ data2;
data1 = data1 ^ data2;
}
从上面的代码我们可以看出少了一个临时变量,同时也加快了代码的运行效率。
尾递归:
递归调用给我们带来了很多方便,也简化了代码,使程序看起来更加的简洁和明了,但递归调用也通常伴随着一个潜在的危险:出栈,接下来我们来看一个常见的递归。
int factorial(int n)
{
if (n < 1)
{
return 1;
}
else
{
return factorial(n-1)*n;
}
}
通常在求一个数的阶乘的时候我们习惯于采用上述方法,然而分析来看当输入的n值较大时,factorial(n-1)*n所计算的值会不断的压入堆栈,生成很多的临时变量,等待下一个的值的确定才得以计算,然而在内存中堆栈的大小是固定的,当输入的n值很大时,极有可能产生堆栈溢出!因此,有一个好的方法解决这种问题即尾递归调用。接下来我们来看这种强大的算法。
int factorial(int n, int m)
{
if (n < 2)
{
return m;
}
else
{
factorial(n-1, n*m);
}
}
从代码中可以看出,通过引入一个新的参数m来存放每次递归所产生的值,这样就避免了每次递归都要进行压栈操作,也就不会产生堆栈溢出的现象,而且普通的递归每次递归只能等待下一次递归得到的结果后才能继续运算,而尾递归每次执行都会进行运算,一次循环执行完毕即可得到结果,其时间复杂度为O(n);