C/C++复习之路——王道程序员求职宝典——第四章《运算符及其优先级》

文章目录

    • i++与++i/i--与--i
    • 异或
    • C++基本类型转换
      • 小范围转大范围
      • 大范围转小范围
      • 数据溢出
      • 表达式转换
      • 显示转换
      • 运算符优先级

i++与++i/i–与--i

  1. i++表示取i的地址,把它的值装入寄存器,然后增加内存中i的值。也就意味着当i++这句话计算完以后,遇到":"后,才将i的数值在对应的地址中更新。而++i则是曲i的地址,并增加它的数值。
    例1:
    int main(void)
    {
        	int a = 3;
        	a += (a++);  //执行后a=7。“=”右边a=4(此时没有将a=4放入a的地址中),“=”左边a=3,因此加完a=7,这句话执行完后,更新a的数据为7.
        	a += (++a);  //执行后a=16。“=”右边a=8(此时将a=4放入a的地址中),“=”左边a=8,因此加完a=16,这句话执行完后,更新a的数据为7.
    }
    
    再比如:
    int main()
    {
    	   int a[] = {1,2,3,4,5,6};
    	   int i=0;
    	   printf("%d", a[i++]);  //执行结果为1,此时相当于输出a[0]的数值,在该语句执行结束后遇到“;”后才更新i的数值为1.
    	   printf("%d", a[++i]);  //执行结果为3,此时相当于输出a[3]的数值,在i++时,将i的数值更新为1+1=2,之后再将a[2]输出。
    }
    
  2. ++i可以用于左值/右值操作,i++只能用于右值操作。
     int main(void)
    {
        int a = 3;
        a += (a++);  //执行后a=7。“=”右边a=4(此时没有将a=4放入a的地址中),“=”左边a=3,因此加完a=7,这句话执行完后,更新a的数据为7.
        a += (++a);  //执行后a=16。“=”右边a=8(此时将a=8放入a的地址中),“=”左边a=8,因此加完a=16,这句话执行完后,更新a的数据为16.
        (++a)  += (a++);  //执行后a=35。“=”左边a=17(此时将a=17放入a的地址中),“=”左边a=18,因此加完a=35,这句话执行完后,更新a的数据为35.
        (a++) += a;  //此句错误,编译不通过,因为不能对a++右值操作.
    

异或

  两个相同的数异或后结果为0,否则为1,且满足交换律:
若A ^B ^C ^D ^E ^F ^D,则其等价于A ^B ^C ^E ^F。这一性质通常用于寻找数成对出现时缺失某一个数。

  • 例1:假设有一个文件,文件中每行记录一个数,且每个数在文件中都出现两次,但某一个数不小心被删除了,问怎么快速找出这个数?
    解析:由于两个数异或的结果为0,故可以依次读入文件中的每个数,并进行异或操作, 最后得到的数,就是不小心删除的数字。

  • 例2:不适用第三方变量,如何交换两个变量的值。
    解析:使用异或。

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

例2:不用算术运算符实现两个数的加法。
解析:
1)先不考虑仅为,安慰计算各位累加(用抑或实现),得值a;
2)然后计算仅为,并将进位的数值左移,得值b。若b为0,则a就是加法运算的结果;若b不为0,则a+b即得结果。

int add_no_arithm(int a, int b)
{
	if (b==0) return a; //当没有进位时
	int sum = a^b;
	int carry = (a&b) <<1;
	return add_no_arithm(sum, carry);
}

例e:如何实现位操作求两个数的平均值?
解析:(x&y)+((x^y)>>1)。如x:01010,y:0110。x&y为0100,即取x、y中对应位都为1的位,并将结果置位1,相当于取得了都为1的位相加的一半。
(x^y)结果为00110,即取x、y中对应为只有一个1的位,并将结果的对应为置1,由于最终结果是平均值,故除以2(用右移1位实现)。

C++基本类型转换

int *ip;
ip=0;

上式中将int类型的0转换为int*类型的空指针。

小范围转大范围

char a=0xe0;
int b=a;  //此时b为0xffffffe0,因为二进制a=11100000,转换成int会作符号位扩展,此时a符号位为1,故补1

大范围转小范围

当前大多数的系统都是讲int低字节赋值给short,而将高位舍去。

short a = 0x1111ffff; //此时a=0xff
unsigned short a = 0x1111ffff; //此时a=0xff

数据溢出

例1:下面两段代码中for分别执行了多少次?

unsigned short i,j;
for(i=0,j=2;i!=j;i+=5,j+=7){}
unsigned short i,j;
for(i=3,j=7;i!=j;i+=3,j+=7){}

解析:第一个执行了32767次,第二个执行了16383次。注意数据类型为unsigned short,题目中j比i大,而且增长快,实际中是不可能相等的。但是当j超出unsigned short(超过65536)时,会发生转换,转换规则为j%65536(即重新从0开始)。故i=j时,j已经超过65536后才可以满足,因此可以列出:(0+5x)+65536=2+7x,解得x=32767。第二段代码同理。

表达式转换

1)整型提升
  在表达式计算中,C++将bool、char、unsigned char、signed char、short和signed short型值都会自动换成int型。
2)运算时的转换
  当运算设计两种类型时,较小的类型将会被转换成较大的类型,由类型大小低到高依次是:

int->unsigned int->long->unsigned long->float->double->long double

例:下面程序的输出结果为()。

unsigned int a=1;
count<<a*-2<<endl;
  • A. -4
  • B. 4
  • C. 4294967294
  • D. 4294967295
    解析:C。考察的是int类型和unsigned int类型一起混合计算,int被转换为unsigned int。-2的补码就是111…10,其中前31位均为1,转换为unsigned int后内容不变,符号位被当做数值位,则是4294967294,乘以a(值为1)后不变。

负数在计算机中通常以补码的形式存储。
int a=5,; //对应二进制位00000000 00000000 00000000 00000101 。
那对于int a=-5;而言那,在存储时需要先求得其源码,再转换成反码,反码+1=补码,补码才是-5在内容中存储的实际二进制数据。
-5对应的源码 10000000 00000000 00000000 00000101
-5对应的反码 11111111 11111111 11111111 11111010 (对源码进行求反,1变0,0变1)
-5对应的补码 11111111 11111111 11111111 11111011 (反码+1)
切记:正数的反码和补码与原码相同,只有负数的不同。

显示转换

例:小端机器下,下面程序的运行结果是什么?

unsigned int a=0x1ffffff7;
unsigned char b=a;  /语句1/
char c=a; //语句2
char* p=(char*)&a;  //语句3
printf("%x, %x, %x",b, c, *p)

解析:f7, fffffff7, fffffff7.
语句1是的b为f7,因为发生截断,将a低字节赋值给b,而将高位舍去。
同理,c也为f7。
但是b为无符号数,c为有符号数,当printf把b、c压入栈时,需要入栈4个字节,需要做符号扩展,b为无符号数,高位补0;c为有符号数,且符号位为1(最高位),因此高位补1。所以最终的输出b=0xf7,c=0xfffffff7。
而p是指向a的字地址(低地址)处,因为是小端机器,故字地址处存放的是f7,printf入栈时,需要入栈4个字节,同样需要对其做符号扩充,因为*p是char类型,且符号位位1,所以高位补1。

运算符优先级

例。设intx=4,则执行以下语句:x+=x-=x-x–;后,x的值为()。

  • A. -1
  • B. 5
  • C. 7
  • D. 11
    解析:C。赋值运算符优先级相同,且结合方向从右向左(对从右向左的运算符,先执行右侧的部分),所以首先计算x-=x-x,得4。然后计算x+=x-=x-x(等价于x+=(x-=x-x)),得8。最后执行x–,故x为7。

例:请写出如下代码的运行结果。

int main()
{
	int a,b,c,d;
	a=0;
	b=1;
	c=2;
	d=3;
	printf("%d", a+++b+c+++d++);
}

解析:该程序的运行结果为6。由于++都是后缀形式,故0+1+2+3=6。注意,这里a++、c++、d++均在本条语句执行结束后才更新。

例。下述程序的运行结果是()。

int main()
{
	int a,b,c;
	c=a=0x30, b=0x60;
	a=c | b >> 4;
	cout<<a<<'_'<<b<<'_'<<c<<endl;
}

解析:54_96_48。移位运算符的优先级较高,因此先执行移位操作,得a=0x36,即54。

例:int a1=x+y-z, int a2=x-z+y;这样的语句是成立的,且a1=a2,,一开始也许会觉得这句话肯定不对,因为会溢出,单子VS2010下a1=a2,这是因为一处会有标识位,减的时候回考虑到这个标识位的作用,因此这句话正确。

例。字节为6的二进制有符号整数,其最小值是()。
解析:-32。此时该6位数为100000,因此对应的数应该是-(011111再取反)=-(100000)=-32。

例。若有以下声明和语句:

struct student{
	int age;	
}std, *p;
p = &std;

则以下对结构体变量std中成员age的引用方式正确的是()(多选)。

  • A. std.age
  • B. p->age
  • C. (*p).age
  • D *p.age
    解析:ABC。“.”的优先级高于“*”,故D错误。

例。以下代码的输出结果为()。

int func(std::vector<int> vec)
{
    static int k=2;
    std::vector<int>::reverse_iterator it;
    for(it=vec.rbegin(); it!=vec.rend(); ++it){
        k += *it%2==0? ++*it: (*it)++; //语句1
    }
    return k;
}
int main(){
    std::vector<int> vec;
    for (int i=0;i<4;i++){
        vec.push_back(i);
        printf("%d\n", func(vec));
    }
    return 0;
}

解析:3 5 10 18。为了更直观的看出来效果,我在每次开始前后将数据输出,修改后代码如下:

#include 
#include 
#include 
using namespace std;
int func(std::vector<int> vec)
{
    cout<<"input:---- "<<endl;
    static int k=2;
    std::vector<int>::reverse_iterator it;
    for(it=vec.rbegin(); it!=vec.rend(); ++it){
        printf("Before: k = %d  it=%d  *it=%d\n", k, it, *it);
        k += *it%2==0? ++*it: (*it)++; //语句1
        printf("After: k = %d  it=%d  *it=%d\n", k, it, *it);
    }
    return k;   
}


int main(){
    std::vector<int> vec;
    for (int i=0;i<4;i++){
        vec.push_back(i);
        printf("Output: k=%d\n", func(vec));
    }
    return 0; 
}

输出结果如下:

input:---- 
Before: k = 2  it=6421920  *it=0
After: k = 3  it=6421928  *it=1
Output: k=3
input:---- 
Before: k = 3  it=6421920  *it=1
After: k = 4  it=6421928  *it=2
Before: k = 4  it=6421920  *it=0
After: k = 5  it=6421928  *it=1
Output: k=5
input:---- 
Before: k = 5  it=6421920  *it=2
After: k = 8  it=6421928  *it=3
Before: k = 8  it=6421920  *it=1
After: k = 9  it=6421928  *it=2
Before: k = 9  it=6421920  *it=0
After: k = 10  it=6421928  *it=1
Output: k=10
input:---- 
Before: k = 10  it=6421920  *it=3
After: k = 13  it=6421928  *it=4
Before: k = 13  it=6421920  *it=2
After: k = 16  it=6421928  *it=3
Before: k = 16  it=6421920  *it=1
After: k = 17  it=6421928  *it=2
Before: k = 17  it=6421920  *it=0
After: k = 18  it=6421928  *it=1
Output: k=18

例。写出float x与零值比较的if语句。
解析:float类型只能保证6位有效数字,double能保证10位有效数字。

if(x>-0.000001 && x<0.000001)

例。计算表达式 x 6 + 4 x 4 + 2 x 3 + x + 1 x^6+4x^4+2x^3+x+1 x6+4x4+2x3+x+1最少需要()次乘法。

  • A. 3
  • B. 4
  • C. 5
  • D, 6
    解析:A。原式= x 2 ∗ ( x 4 + 4 ∗ x 2 + 2 ∗ x ) + x + 1 x^2*(x^4+4*x^2+2*x)+x+1 x2(x4+4x2+2x)+x+1 x 2 x^2 x2用一次乘法, x 4 x^4 x4视为 ( x 2 ) 2 (x^2)^2 (x2)2,这是第二次乘法,外面的 x 2 ∗ ( ) x^2*() x2()是第三次乘法,所有常系数的乘法都展开成连加或用位移运算完成。

你可能感兴趣的:(C/C++,运算符,c++,C,编程语言)