c語言自加自減三道題

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

x = 0;函數

y = z = -1;post

x += -z ---y;測試

printf(「x=%d\n」,x)spa

x = 2指針

爲何?orm

x  + = -z - - -y 至關於 xml

x = x + ((-z)--)-y; 這裏-z—是先用-z而後再(-z)- -運算htm

這裏須要注意的是操做符結合的順序是自左至右,而運算順序是自右至左!blog

也就是 –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

運行結果正如咱們所示。

 

轉載於:https://www.cnblogs.com/AI-Algorithms/p/3790628.html