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 自加以後,而後此時i是5,再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) :先處理括號內的運算
++x,x的值+1變爲3
x++,暫時不變
++x,x的值+1變爲4
如今至關於y=x+x+x=4+4+4=12;x再加1=5;
z=(x--)+(--x)+(x--):先處理括號內的運算
x--,暫時不變
--x,x變爲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的後置自增自減值,具體計算過程以下:
還有一種就是:
I = 2;
printf(「%d,%d,%d」,++I,i++,++i); 這輸出的結果究竟是什麼? 答案是 5,3,5
在這裏是先計算最右邊的那個 ++I 那麼此時的I 就變成了3,而後計算中間那個i++,當碰到後置運算時,編譯器會申請一個零時寄存器把當前的i爲3的值保存起來,當前記爲寄存器1,而後緊接着進行i++計算,此時I的值爲4,而後計算最左邊那個++i,此時i加出來的值爲5, 此時i值被保存爲5,而後入棧,在入棧中保持兩個++i的寄存器的值都是從新經過mov指令將當前的i值mov到這些寄存器中而後入棧的,因此此時第一個++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運行結果:
vs2010運行結果:
運行結果正如咱們所示。