/* 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
a=a+b; //a存变量之和
b=a-b; //和-原b得到a
a=a-b; //和-原a得到b
用a将a,b的积存储起来,之后利用乘法,b换成a,a再换成b
a=a*b; //a存变量之积
b=a/b; //积除以原b得到a
a=a/b; //积除以原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而已
}
类似的应用如快速排序。
声明:除已注明的引用外,文章系作者原创,引用转载请注明出处,如有必要请联系作者