zoukankan      html  css  js  c++  java
  • c语言自加自减三道题

    int  x , y,z;

    x = 0;

    y = z = -1;

    x += -z ---y;

    printf(“x=%d ”,x)

    x = 2

    为什么?

    x  + = -z - - -y 相当于

    x = x + ((-z)--)-y; 这里-z—是先用-z然后再(-z)- -运算

    这里需要注意的是操作符结合的顺序是自左至右,而运算顺序是自右至左!

    也就是 –z - - -y 表示的是 ((-z---y 而不是 (-z)-(--y)


    #include <stdio.h>

     

    int main()

    {

            

             struct st

             {

                       int n;

                       struct st *next;

             };

             struct st a[3],*p1,*p2,*p3,*p4;

             a[0].n = 5;

             a[0].next = &a[1];

             a[1].n = 7;

             a[1].next = &a[2];

             a[2].n = 9;

             a[2].next = '';

             p1 = p2 = p3 =p4 = &a[0];

            

             printf("p1++->n = %d ",p1++->n);

             printf("p1->n = %d ",p1->n);

     

             //printf("p2->n++ = %d ",p2->n++);

             //printf("p2->n = %d",p2->n);

     

             //printf("(*p3).n++ = %d ",(*p3).n++);

             //printf("(*p3).n = %d ",(*p3).n);

     

             //printf("++p4->n = %d ",++p4->n);      

    }

    第一组

    printf("p1++->n = %d ",p1++->n); //p1++>n = a[0].n = 5

    printf("p1->n = %d ",p1->n); //此时在上一句执行完之后 p1++->n中的p1执行了p1++操作,p1是指针,它每增加一个单位就是增加一个指针位,所以p1++就使p1指向了a[0]下一个变量的地址,即a[1]的地址,此时p1->n就是a[1].n的值,所以此时 p1->n = a[1].n = 7

     

    第二组

    printf("p2->n++ = %d ",p2->n++); //p2->n++ = 5 注意 p2也是指向a[0]的,所以p2->n = a[0].n,然后执行完只一句之后,就执行了p2->n++ 执行加加操作, 此时其实操作的a[0].n的值自加1,那么 p2->n++之后,a[0].n的值由原来的5就变成了6

    printf("p2->n = %d",p2->n); //p2->n = 6, 这里等于6 是因为上面加加了。

     

    第三组

    printf("(*p3).n++ = %d ",(*p3).n++); //(*p3).n++ = 5 这里的 *p3 就相当于a[0],所以(*p3).n = a[0].n 也就等于5

    printf("(*p3).n = %d ",(*p3).n); //这里在上面的(*p3).n++之后 a[0].n 变成了6.

     

    第四组

    printf("++p4->n = %d ",++p4->n); //这里的 ++p4->n = 6 为什么? 明显 p4->n相当于a[0].n 因为这里的 –> 的优先级高于 ++ , -- 等操作符,所以要先结合 ->操作,所以++p4->n 就相当于++(p4->n)

             printf("a[0].n = %d ",a[0].n); 这个语句在上面的p4测试之后加测的一句,证明a[0].n = 6 也就是证明 ++(p4->n) 是分析正确的。

     

    上面这个问题其实就是操作符优先级的问题,具体可以参考c语言操作符优先级问题


     

    #include <stdio.h>

    int main()

    {

             int x = 2,y,z;

             int i = 2;

             printf("++i=%d,i++=%d,++i=%d ",++i,i++,++i);

             printf(“I = %d”,i);

             y = (++x)+(x++)+(++x);

             z = x--+--x+x--;

             printf("x=%d,y=%d,z=%d",x,y,z);

             return(0);

    }

     

    printf("++i=%d,i++=%d,++i=%d ",++i,i++,++i);  // 5, 3, 5

    printf(“I = %d ”,i); // 6

    输出结果是 

    ++I = 5,i++ =3, ++I = 5  这表明 先是是第一个 ++I 和和 第三个++I 自加之后,然后此时i5,再printf()时就是 5,3,5,然后printf()之后, i又自加了,所以后面那个I = 6

     

    printf("x=%d,y=%d,z=%d",x,y,z); // x = 2, y = 12,z = 12;

    首先明白:

    ++在前边是,先自加1再使用

    ++在后边是先使用数值,再自加1

     

    y=(++x)+(x++)+(++x);

    i++是先使用i本身的值,再把i+1

    ++i是先把i+1,再使用i的值运算。

     

    y=(++x)+(x++)+(++x)  :先处理括号内的运算

    ++xx的值+1变为3

    x++,暂时不变

    ++xx的值+1变为4

    现在相当于y=x+x+x=4+4+4=12x再加1=5

    z=(x--)+(--x)+(x--):先处理括号内的运算

    x--,暂时不变

    --xx变为4

    x--,暂时不变

    z=x+x+x=4+4+4=12

    然后x自减两次变为2

    
    
    
     y=++x;y+=x++;y+=++x;z=x--;z+=--x;z+=x--;这样才是正确的使用

    补充:

    别算了,在TC里面结果是2,12,12,;在VC里面是2,10,12,

    补充:

     自加自减在复杂表达式中的处理方法
    3. 1
     前加分解法
    i1, i2, ?, in , s 均为变量, op 为运算符, 且有表达式s = (+ + i1)op (+ + i2)op ?op (+ + in) (3—1)
    op 为逻辑与运算符(&&) , 且存在im (1 ≤m ≤n) ,使得im + 1 = 0, ik + 1 ≠ 0 (1 ≤ k ≤m ) , 则表达
    (3—1) 可分解为
    i1 = i1 + 1; i2 = i2 + 1; ...; im = im + 1;
    s = 0
    否则, 表达式(3—1) 可分解为
    i1 = i1 + 1; i2 = i2 + 1; ...; in = in + 1;
    s = 1
    op 为逻辑或运算符(‖) , 且存在im (1 ≤m ≤ n) ,使得im + 1 ≠ 0, ik + 1 = 0 (1 ≤ k ≤m ) , 则表达
    (3—1) 可分解为
    i1 = i1 + 1; i2 = i2 + 1; ...; im = im + 1;
    s = 1
    否则, 表达式(3—1) 可分解为
    i1 = i1 + 1; i2 = i2 + 1; ...; in = in + 1;
    s = 0
    op 双目算术运算符, 则表达式(3—1) 可分解为
    i1 = i1 + 1; i2 = i2 + 1; ...; in = in + 1;
    s = i1 op i2 op ... op in;
    3. 2
     后加分解法
    i1, i2, ..., in , s 均为变量, op 为运算符, 且有表达式s = ( i1 + + )op ( i2 + + )op ...op ( in + + ) (3—2)
    op 为逻辑与运算符(&&) , 且存在im (1 ≤m ≤n) ,使得im = 0, ik ≠ 0 (1 ≤ k ≤m ) , 则表达式(3—2)
    可分解为
    s = 0
    i1 = i1 + 1; i2 = i2 + 1; ...; im = im + 1;
    否则, 表达式(3—2) 可分解为
    s = 1
    i1 = i1 + 1; i2 = i2 + 1; ...; in = in + 1;
    op 为逻辑或运算符(‖) , 且存在im (1 ≤m ≤ n) ,使得im ≠ 0, ik = 0 (1 ≤ k ≤m ) , 则表达式(3—2)
    可分解为
    s = 1
    i1 = i1 + 1; i2 = i2 + 1; ...; im = im + 1;
    否则, 表达式(3—2) 可分解为
    s = 0
    i1 = i1 + 1; i2 = i2 + 1; ...; in = in + 1;
    op 双目算术运算符, 则表达式(3—2) 可分解为
    s = i1 op i2 op ... op in;
    i1 = i1 + 1; i2 = i2 + 1; ...; in = in + 1;
    3. 3
     前减分解法
    i1, i2, ..., in , s 均为变量, op 为运算符, 且有表达式
    s = (- - i1)op (- - i2)op ...op (- - in) (3—3)
    op 为逻辑与运算符(&&) , 且存在im (1 ≤m ≤n) ,使得im - 1 = 0, ik - 1 ≠ 0 (1 ≤ k ≤m ) , 则表达
    (3—3) 可分解为
    i1 = i1 - 1; i2 = i2 - 1; ...; im = im - 1;
    s = 0
    否则, 表达式(3—3) 可分解为
    i1 = i1 - 1; i2 = i2 - 1; ...; in = in - 1;
    s = 1
    op 为逻辑或运算符(‖) , 且存在im (1 ≤m ≤ n) ,使得im - 1 ≠ 0, ik - 1 = 0 (1 ≤ k ≤m ) , 则表达
    (3—3) 可分解为
    i1 = i1 + - 1; i2 = i2 - 1; ...; im = im - 1;
    s = 1
    否则, 表达式(3—3) 可分解为
    i1 = i1 - 1; i2 = i2 - 1; ...; in = in - 1;
    s = 0
    op 双目算术运算符, 则表达式(3—3) 可分解为
    i1 = i1 - 1; i2 = i2 - 1; ...; in = in - 1;
    s = i1 op i2 op ...op in;
    3. 4
     后减分解法
    i1, i2, ..., in , s 均为变量, op 为运算符, 且有表达式
    s = ( i1 - - )op ( i2 - - )op ...op ( in - - ) (3—4)
    op 为逻辑与运算符(&&) , 且存在im (1 ≤m ≤n) ,使得im = = 0, ik ≠ 0 (1 ≤ k ≤ m ) , 则表达式(3—4) 可分解为
    s = 0
    i1 = i1 - 1; i2 = i2 - 1; ...; im = im - 1;
    否则, 表达式(3—4) 可分解为
    s = 1
    i1 = i1 - 1; i2 = i2 - 1; ...; in = in - 1;
    op 为逻辑或运算符(‖) , 且存在im (1 ≤m ≤ n) ,使得im ≠ 0, ik = 0 (1 ≤ k ≤m ) , 则表达式(3—4)可分解为
    s = 1
    i1 = i1 - 1; i2 = i2 - 1; ...; im = im - 1;
    否则, 表达式(3—4) 可分解为
    s = 0
    i1 = i1 - 1; i2 = i2 - 1; ...; in = in - 1;
    op 双目算术运算符, 则表达式(3—4) 可分解为
    s = i1op i2op ...op in;
    i1 = i1 - 1; i2 = i2 - 1; ...; in = in - 1;
    op 为其他C 运算符, 则根据其优先级、结合方向和前() () 依次进行运算即可。一个表达式可能是上述几种表达式的复合, 但只要按上述分解方法, 不管表达式中有多少个自加自减运算, 无论含自加自减运算的表达式多么复杂,即使初学者也会运用自如。
    4
     举例
    1 若有说明
    in t k = 3, s;
    则语句
    s = (+ + k ) + (+ + k) + (k - - ) + (k + + ) ;
    可分解为
    k = k + 1; k = k + 1;
    s = k + k + k + k;
    k = k - 1; k = k + 1;
    由此可知, k 的值为5, s 的值为20
    2 复合语句
    {s = 3
    p 1 + + ; 3
    p 1 = 3
    p 2 - - ; 3
    p 2 = s; }
    可分解为
    {s = 3
    p 1; p 1 = + + ; 3
    p 1 = 3
    p 2; p 222; 3
    p 2 = s; }

    补充:

    y=(++x)+(x++)+(++x);/*x=x+1;x=x+1;y=x+x+x;x=x+1;*/z=(x--)+(--x)+(x--);/*x=x-1;z=x+x+x;x=x-1;x=x-1*/说明:++x的优先级最高,其次到(),最后才是x++


    总结

    分两种情况:

    一种是在一个表达式中出现多个自增自减及加减混合运算,此时怎么算?如下:

    x = 2;

    y = ++x+x--+(++x)+x+++(--x) 等于多少?最后x等于多少?

    面对這样的式子运算方向:先计算所有的前置自增及自减运算,此时计算出来的x值就是式子中每个x的值,此时去掉所有的自增自减符号计算y的值,最后计算x的后置自增自减值,具体计算过程如下:

    clip_image002

    还有一种就是:

    I = 2;

    printf(“%d,%d,%d”,++I,i++,++i); 这输出的结果到底是什么? 答案是 5,3,5

    在这里是先计算最右边的那个 ++I 那么此时的I 就变成了3,然后计算中间那个i++,当碰到后置运算时,编译器会申请一个零时寄存器把当前的i3的值保存起来,当前记为寄存器1,然后紧接着进行i++计算,此时I的值为4,然后计算最左边那个++i,此时i加出来的值为5 此时i值被保存为5,然后入栈,在入栈中保持两个++i的寄存器的值都是重新通过mov指令将当前的imov到这些寄存器中然后入栈的,所以此时第一个++i和最后一个++i入栈的值都为5 而中间那个i++入栈的值则是从零时寄存器1直接入栈,所以此时的i++位置的值为3,而printf(“%d,,,,”,++I,i++,i++)中这后面的参数表示++I,i++,++i所代表的地址中的值,然printf时,会去这些地址中取值,当时之前入栈的时候,往这些地址中写入值的时候是写入的5,3,5,所以这里取出来时也会是5,3,5,所以打印出来的就是,5,3,5.

     

    c语言在函数参数的入栈方向上是自右向左的,如下printf()是先计算++j,然后再计算j++,然后再计算i++,…….

         printf("i++=%d,i++=%d,i++=%d,j++=%d,++j=%d ",i++,i++,i++,j++,++j);

    00F237BD  mov         eax,dword ptr [j] 

    00F237C0  add         eax,1 

    00F237C3  mov         dword ptr [j],eax 

    00F237C6  mov         ecx,dword ptr [j] 

    00F237C9  mov         dword ptr [ebp-0E8h],ecx 

    00F237CF  mov         edx,dword ptr [j] 

    00F237D2  add         edx,1 

    00F237D5  mov         dword ptr [j],edx 

    00F237D8  mov         eax,dword ptr [i] 

    00F237DB  mov         dword ptr [ebp-0ECh],eax 

    00F237E1  mov         ecx,dword ptr [i] 

    00F237E4  add         ecx,1 

    00F237E7  mov         dword ptr [i],ecx 

    00F237EA  mov         edx,dword ptr [i] 

    00F237ED  mov         dword ptr [ebp-0F0h],edx 

    00F237F3  mov         eax,dword ptr [i] 

    00F237F6  add         eax,1 

    00F237F9  mov         dword ptr [i],eax 

    00F237FC  mov         ecx,dword ptr [i] 

    00F237FF  mov         dword ptr [ebp-0F4h],ecx 

    00F23805  mov         edx,dword ptr [i] 

    00F23808  add         edx,1 

    00F2380B  mov         dword ptr [i],edx 

    00F2380E  mov         esi,esp 

    00F23810  mov         eax,dword ptr [j] 

    00F23813  push        eax 

    00F23814  mov         ecx,dword ptr [ebp-0E8h] 

    00F2381A  push        ecx 

    00F2381B  mov         edx,dword ptr [ebp-0ECh] 

    00F23821  push        edx 

    00F23822  mov         eax,dword ptr [ebp-0F0h] 

    00F23828  push        eax 

    00F23829  mov         ecx,dword ptr [ebp-0F4h] 

    00F2382F  push        ecx 

    00F23830  push        offset string "i++=%d,i++=%d,i++=%d,j++=%d,++j="... (0F25A60h) 

    00F23835  call        dword ptr [__imp__printf (0F282B4h)] 

    00F2383B  add         esp,18h 

    00F2383E  cmp         esi,esp 

    00F23840  call        @ILT+305(__RTC_CheckEsp) (0F21136h)

     

    总结


    当计算式为一个表达式,表达式中有前置/后置自增自减/加减混合运算时:

    Step1:从表达式左边到右边用括号括出前置/后置自增自减运算符

    Step2:从表达式左边到右边依次计算所有前置运算符,计算某个变量x的最终值为当前表达式中所有这个变量x的当前值

    Step3:从表达式左边至右边进行加减运算符计算,计算所得值为当前表达式的值

    Step4:从表达式左边到右边分别计算各个变量的后置运算符,计算所得变量值,为各变量最终值

    例如:

    x = 1 y =2;

    z = ++x+x--+(--y)+(++x)+y+++(--x)+(++y);

    下面分步解析:

    Step1:用括号自左至右括出自增自减前置后置运算符,下面分步解析:

    1. z = (++x)+x--+(--y)+(++x)+y+++(--x)….

    2. z = (++x)+(x--)+(--y)+(++x)+y+++(--x)….

    3. z = (++x)+ (x--)+(--y)+(++x)+y+++(--x)….

    ……

    Step1最终形式:

    z = (++x)+(x--)+(--y)+ (++x)+ (y++)+(--x)+ (++y);

     

    Step2:自表达式左边至右边计算所有的前置运算:

    所有的前置运算如:

    z = (++x)+(x--)+(--y)+ (++x)+ (y++)+(--x)+ (++y);

    注意自左至右计算,x初始值为1,先算++x 得到x=2,然后算中间的++x 此时得到x = 3,然后再算最后的—x 得到x =2,至此变量x的所有前置运算结束,此时z表达式中出现变量x的地方,它们的值都是2.,此时z表达式可“简化”为:

    z = 2+2+(--y)+ 2+ (y++)+2+ (++y);

    计算y的所有前置运算:y初始值为2,自左至右计算,先算—y得到y=1,再算++y得到y=2,此时z表达式中所有y的值均表示为2,此时z表达式可进一步“简化“为:

    z = 2+2+2+ 2+ 2+2+ 2;

    Step3:从表达式左边到右边计算加减运算来计算z表达式的值,显然z = 2*7 =14

    Step4: 从表达式左边到右边分别计算各个变量的后置运算符,计算所得变量值,为各变量最终值

    所有前置运算之后,x=2,y=2,此时为简化z表达式中后置运算如:

    z = (++x)+(x--)+(--y)+ (++x)+ (y++)+(--x)+ (++y);

    x进行后置计算 x = 1,y进行后置运算得y=3

    从上面可以看出表达式计算一直都是从左边计算到右边的!

    下面是函数参数的运算:这个是从右边到左边的!

    I = j = 2;

    printf("++i=%d ,--i=%d,i++=%d,i++=%d, ++i=%d ,j++=%d,++j=%d ",++i,--i,i++,i++,++i,j++,++j);

    这里先计算 ++j,然后计算j++,然后依次计算++i,i++,i++,--I,++i

    这里需要注意的是,在前置运算时,对变量进行的前置运算会是全局的,就是说按照上面的计算对i的前置计算顺序是 ++I,然后—I,最后++I,這样计算之后,在最后计算++i之后得到的i值将是这”++I,--I,++i”三个位置的值,就是这三个位置的值是一样的,而碰到后置运算时,会先申请一个临时地址或寄存器把当前变量值存储起来,然后再计算其后置运算。

    下面详细分析其计算过程

    printf("++i=%d ,--i=%d,i++=%d,i++=%d, ++i=%d , --j=%d j++=%d,++j=%d ",++i,--i,i++,i++,++i,--j,j++,++j);

    c语言函数参数入栈顺序是自右向左,所以这里的计算顺序是自右向左,所以从++j开始计算。

    首先I = j  = 2 初始化都为2

    第一步计算++j 是前置计算,计算值直接复制给j 所以第一步的结果是 j = 3

    第二步计算j++,这里遇到后置计算,计算前先甚至一个临时寄存器假设为Reg1,保留当前j的值,然后再进行后置计算,所以这里又分两步:第一步申请临时寄存器Reg1保存j=3的值,然后j后置加加,结果是 Reg1 = 3 ,j = 4

    第三步计算--j,这里是前置计算,所以直接计算 –j 得到j = 3

    注:这里j的三个表达式计算完了,这里第三步计算出来的j的值将赋值给第一步的计算值,也就是输出结果时第一步和第三步是一样的, 当然这里刚好第一步自身计算的值也和第三步是一样的。第二步输出的值就是那个Reg1的值,所以

    printf(“……--j=%d,j++=%d,++j=%d”,…..,--j,j++,++j)实际输出是

    printf(“……--j=%d,j++=%d,++j=%d”,…..,j=3,Reg1=3,j=3)

    下面接着分析:

    第四步计算++i 前置运算直接算,得到 i = 3

    第五步计算i++,后置计算,要先申请临时寄存器保存当前值,然后再计算,申请Reg2 = i =3,然后i++ i=4,

    第六步计算i++,还是后置计算,还是先申请临时寄存器保存当前i=4的值,然后再进行后置计算,那么就是Reg3 = i = 4, 然后i++ ,得到i = 5

    第七步计算--i 前置计算,直接计算得到i = 4

    第八步计算++i,前置计算,直接计算得到 i = 5

    之前说了,所有的前置计算具有全局性,就是变量i有多个前置运算表达式,那么计算完所有的变量i的前置运算后,所有前置运算表达式中的i值就是这个最终计算的值,第八步计算的i= 5 i的前置运算分布在第四步,第七步,第八步,那么这三步目前的表达式中i都是i = 5,这就是所谓的全局性,而后置运算表达式的i则是其临时寄存器中的值,第五步中i++的变量i的值现在是其临时寄存器Reg2的值,也就是i = 3, 第六步中的i值也就是Reg3中的值,也就是i = 4

    ,所以最终输出结果是:

    gcc运行结果:

    clip_image003

    vs2010运行结果:

    clip_image003

    运行结果正如我们所示。

     

  • 相关阅读:
    Java Formatter 阅读心得
    Android 应用的动画实践View Animation篇
    Hexo 简明入门教程(一)
    用Gradle 构建你的android程序依赖管理篇
    MQTT 折腾笔记协议简读
    ingress 在中国大众篇
    谈谈常见的移动应用设计风格
    用nodejs 改造一个移动版本的网站
    Android AdapterView 源码分析以及其相关回收机制的分析
    关于计算机类课程实验教学的思考
  • 原文地址:https://www.cnblogs.com/AI-Algorithms/p/3790628.html
Copyright © 2011-2022 走看看