首页 > 代码库 > c语言自加自减三道题

c语言自加自减三道题

int  x , y,z;<?xml:namespace prefix = o />

x = 0;

y = z = -1;

x += -z ---y;

printf(“x=%d\n”,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 = ‘\0‘;

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

        

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

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

 

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

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

 

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

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

 

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

}

第一组

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

printf("p1->n = %d\n",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\n",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\n",(*p3).n++); //(*p3).n++ = 5 这里的 *p3 就相当于a[0],所以(*p3).n = a[0].n 也就等于5

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

 

第四组

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

         printf("a[0].n = %d\n",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\n",++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\n",++i,i++,++i);  // 5, 3, 5

printf(“I = %d\n”,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\n",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\n",++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\n",++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

运行结果正如我们所示。