编程中的小技巧(持续更新)

  /*  5. 不使用sizeof()运算符,计算各基本数据类型所占字节数

    在加减运算中,指针的可以“认为”是一个整数。实际上,C语言规定,两个同型指针之差的返回值为int型,但是C语言不支持两只指针相加,报错:error C2110: cannot add two pointers。

Exercise

#include<stdio.h>

void main()
{
	double a[2];

	char *p1=(char *)a,*p2=(char *)(a+1);
	printf("p1的值为%p,p2的值为%p\n",p1,p2);      //%p 以16进制显示指针的值

	int length=p2-p1;
	printf("所占字节数为:%d\n",length);
}

    结果:

a的值为0018FF38,a+1的值为0018FF40         
p1的值为0018FF38,p2的值为0018FF40         //可以明显看出相差 40H-38H = 16-8(十进制) =8
所占字节数为:8 

    上面除了验证指针加整数后指针本身的值其实加 n*sizeof(类型) 外,也验证了两个指针之差返回值为int,另外,本例也提供了除用sizeof()运算符之外,利用指针特性自行计算各变量类型所占字节的新思路。当然,前提是知道char类型占1字节。


PS:C语言不支持两个指针相加的运算,会报错:error C2110: cannot add two pointers

*/



4. 不使用中间变量交换a,b的值

① "a=a+b"

用a将a,b的和存储起来,之后利用减法,b换成a,a再换成b

扫描二维码关注公众号,回复: 1568472 查看本文章

a=a+b;     //a存变量之和

b=a-b;      //和-原b得到a

a=a-b;      //和-原a得到b



②"a=a*b "

用a将a,b的积存储起来,之后利用乘法,b换成a,a再换成b

a=a*b;     //a存变量之积

b=a/b;      //积除以原b得到a

a=a/b;      //积除以原a得到b




③"a=a^b"

    利用C语言位运算符异或--"^"


    a=a^b;

    b=a^b;

    a=a^b;


背景知识:

异或,bit1和bit2不同则为1,相同则为0: 1^0=1;0^1=1;0^0=0;1^1=0;

可以得到两个规律:1)bit和0异或还是bit:  b^0=b

                              2)bit和自己异或得零:  b^b=0

并且异或满足交换律和结合律,即异或式的值只与式中各因子的个数有关,与位置无关

又因为异或本身是位运算符,不产生进位则我们能进一步得到以下结论:

对于等长多位(字节)变量a,b,有: a^a=0; a^0=a;b^0=b;

                                         进而有:  a^b^a=a^a^b=0^b=b; a^b^b=a^0=a;

即有:

a^a^b=b; 
a^b^b=a;


故有代码:

a=a^b;

b=a^b;

a=a^b;


PS:对应异或的还有同或,当然,大多数语言并不提供同或运算符,因为  同或=!(^),

同或的规则和异或相反(按位),假设同或运算符为#,则故有:0#0=1;1#0=0;1#1=1;

推广至多位,则a#0=对a取反;a#a=全1;

或者由a^0=a;a^a=0;而同或#是异或^的非,故对a取反,对0(更准确地说应该是全0)取反,也可以得到相同结论。


三种方法都可以是实现交换,但是a+b和a*b(特别是a*b)的可能导致溢出(如a,b值接近于存储类型最大值),而a^b则不会有此问题,且在CPU中,移位运算要比加法,乘法快的多(具体速度:移位>加法>乘法),所以无论是从溢出可能性还是运算效率考虑,都是异或操作a^b的方法更好。






3. 将k位整数循环右移n位

    由2的知识当然知道可以将k位整数各位数字取出放进数组,循环右移后计算其值(位数在取出数字时已经知道)。

    但是这样的方法效率低下(取出数字并3次逆置),可以考虑使用C语言本来就支持的右移<<运算符:

如:110 0101 101 循环右移三位

—>:101 110 0101

右移3位:                      000 0101 101  (>>运算符默认高位补0)  (中间变量存储)

左移10-3=7位:            101 0000 000  (<<运算符默认低位补0)  (中间变量存储)

相加(或者逐位相或):101 0101 101                

    即通过<<,>>将“两部分”分别保存在相应位置,再相加/或,即可。因为<<,>>运算极快,所以这样的方法

效率极高。



2.  将数组元素循环右移

    eg:1,2,3,4,5,6,7,8,9  循环右移三位

    —>:7,8,9,1,2,3,4,5,6


方法:

将1~6逆置:6,5,4,3,2,1

将7~9逆置:6,5,4,3,2,1,9,8,7

全部逆置:7,8,9,1,2,3,4,5,6






1. 不分奇偶讨论进行数组倒置

a[6]:  0 , 1 , 2    ,     3 , 4 , 5             //MaxSize=6

6/2=3;

则(i=0; i<MaxSize/2; i++)


a[7]:  0 , 1 , 2 ,      3      , 4 , 5 ,6      //MaxSize=6

7/2=3.5

则(i=0; i<MaxSize/2; i++)         //a[3]要和自己交换一下


故有:

 
 
for(i=0;i<MaxSize/2;i++)
{
  temp=a[i];
  a[i]=a[MaxSize-1-i];       //由0+5=1+4=2+3=5可以归纳出对称位置为MaxSize-1-i
  a[MaxSize-1-i]=temp;
}


① 若考虑到奇数时如a[7]交换中间数不再与自己交换,可以设置为i<=MaxSize/2-1

for(i=0;i<=MaxSize/2-1;i++)
{
  temp=a[i];
  a[i]=a[MaxSize-1-i];       //由0+5=1+4=2+3=5可以归纳出对称位置为MaxSize-1-i
  a[MaxSize-1-i]=temp;
}

② 最普通也是最不容易出错的方法:分奇偶数讨论

if(MaxSize%2==0)
  bound=MaxSize/2;
else
  bound=(MaxSize-1)/2;        //这样算出来的边界必然是整数,且i<bound就行
for(i=0;i<bound;i++)
{
  temp=a[i];
  a[i]=a[MaxSize-1-i];       //由0+5=1+4=2+3=5可以归纳出对称位置为MaxSize-1-i
  a[MaxSize-1-i]=temp;
}

③ 更为稳妥的方法:两点法i和j相向而行

for(i=0,j=MaxSize-1;i<j;i++,j--)
{
  temp=a[i];
  a[i]=a[j];      
  a[j]=temp;   // 多用了一个存储空间j而已
}

    类似的应用如快速排序。











声明:除已注明的引用外,文章系作者原创,引用转载请注明出处,如有必要请联系作者

猜你喜欢

转载自blog.csdn.net/prolayman/article/details/79793015