C 語言基礎教程(我的C之旅開始了)[九]

來源:互聯網
上載者:User

24. +、-、*、/、= 的優先順序

1. 優先順序

和數學一樣,C 語言規定先乘除後加減。也就是說,乘法運算子和除法運算子的優先順序(Precedence)比加法運算子和減法運算子高。同時,C 語言也規定,如果兩個運算子的優先順序相同,並且它們之間沒有被優先順序比它們高或者低的運算子隔開,則它們的運算順序根據它們在語句中出現的先後而定。大多數運算子都是從左向右進行運算的,不過也有從右向左進行運算的(例如賦值運算子)。乘法運算子和除法運算子的優先順序相同,加法運算子和減法運算子的優先順序相同。因此,以下語句

var = 8.0 + 20.0 / 4.0 * 2.0;

運算順序為:

20.0 / 4.0
5.0 * 2.0 (20.0 / 4.0 得 5.0)
8.0 + 10.0
var = 18.0

在這個運算式中,/ 和 * 優先順序相同,而且是從左向右進行運算的,所以先運算 20.0 / 4.0,然後才輪到 5.0 * 2.0。

如果我們想讓加法先進行,可以給 8.0 + 20.0 加上括弧

var = (8.0 + 20.0) / 4.0 * 2.0;

這個語句的運算順序為:

8.0 + 20.0
28.0 / 4.0
7.0 * 2.0
var = 14.0

C 語言規定,先進行括弧裡面的運算,後進行括弧外面的運算。在括弧裡面,運算順序和上面討論的一樣。例如:

var = (8.0 + 20.0 / 4.0 * 2.0) / 3.0;

運算順序為:

20.0 / 4.0
5.0 * 2.0
8.0 + 10.0
18.0 / 3.0
var = 6.0

下表總結了這幾個運算子的優先順序以及它們的結合律,按優先順序從高到低進行排列

運算子 結合律

() 從左向右
+ -(單目) 從右向左
* / 從左向右
+ -(二目) 從左向右
= 從右向左

2. 優先順序和運算順序

運算子優先順序(Operator precedence)是決定運算順序的重要規則,但不能完全(也沒必要完全)確定運算順序。例如:

5 * 3 + 8 * 4;

根據運算子優先順序,我們知道,乘法運算先於加法運算。但是 5 * 3 和 8 * 4 誰先誰後,我們並不能確定。它們運算的先後是由編譯器決定的。這是因為某種運算順序在某種系統中效率更高,而另一種運算順序在另一種系統中效率更高。無論它們的運算先後如何,最終得到的結果都是 47。

您可能會說:“乘法不是從左向右進行運算的嗎?這不是說明最左邊的乘法最先進行嗎?”是的,乘法的確是從左向右進行運算,但是您也要看到,這兩個乘法運算子之間被加法運算子隔開了!我們舉一個例子來說明乘法從左向右進行運算的意思。以下語句

5 * 2 * 9 * 4;

運算順序為:

5 * 2
10 * 9
90 * 4

下面我們來看一個小程式。

/* precedence.c -- 優先順序測試 */
#include <stdio.h>

int main(void)
{
int var1, var2;

var1 = var2 = -(9 + 4) * 5 + (6 + 8 * (7 - 1));
printf("var1 = var2 = %d\n", var1);

return 0;
}

請認真閱讀以上程式,想想會出現什麼結果,然後編譯運行,看看運行結果和您想象的是否一樣。

首先,括弧運算子優先順序最高。但是 (9 + 4) 和 (6 + 8 * (7 - 1)) 運算的先後是由編譯器決定的。假設 (9 + 4) 先進行,則運算後得 13,然後負號運算子作用於 13 得 -13。於是我們得到:

var1 = var2 = -13 * 5 + (6 + 8 * (7 - 1));

在 (6 + 8 * (7 - 1)) 中,先運算 (7 - 1),得:

var1 = var2 = -13 * 5 + (6 + 8 * 6);

因為 * 優先順序高於 +,於是我們得到:

var1 = var2 = -13 * 5 + (6 + 48);

進而

var1 = var2 = -13 * 5 + 54;
var1 = var2 = -65 + 54;
var1 = var2 = -11;

因為賦值運算是從右向左的,所以 -11 被賦值給 var2,接著 var2 被賦值給 var1。最終的結果是,var1 和 var2 相等,它們的值都是 -11。

25. 模除運算子 %

% 是模除運算子(Modulus Operator),用於求餘數。% 只可用於對整數進行模除,不可用於浮點數。例如:

15 % 2 // 正確。餘數為 1
15.2 % 3 // 錯誤!

C99 以前,並沒有規定如果運算元中有負數,模除的結果會是什麼。C99 規定,如果 % 左邊的運算元是正數,模除的結果也是正數;如果 % 左邊的運算元是負數,模除的結果就是負數。例如:

15 % 2 // 餘 1
15 % -2 // 餘 1
-15 % 2 // 餘 -1
-15 % -2 // 餘 -1

標準規定,如果 a 和 b 都是整數,則 a % b 可以用公式 a - (a / b) * b 算出。例如:

-15 % 2 == -15 - (-15 / 2) * 2 == -15 - (-7) * 2 == -1

最後,我們看一個小程式。

/* months_to_year.c -- 將使用者輸入的月數轉換成年數和月數 */

#include <stdio.h>

int main(void)
{
int months, years, months_left, months_per_year = 12;

printf("Enter the number of months: ");
scanf("%d", &months);

years = months / months_per_year; /* 算出年數 */
months_left = months % months_per_year; /* 算出剩餘的月數 */

printf("%d months is %d years, %d months.\n", months, years, months_left);

return 0;
}

26. 自增運算子和自減運算子

1. 自增運算子(Increment Operator)

自增運算子 ++ 使運算元的值增 1。++ 可以置於運算元前面,也可以放在後面。例如:

++n ;
n++ ;

這兩個語句產生的結果都是使 n 增 1,可以說沒什麼區別。使用以下語句得到的效果也是一樣的:

n = n + 1 ;

儘管上面兩個語句中,++ 前置和後置沒有區別。但是,++ 前置和後置其實是有區別的。例如:

int n = 1, post, pre;

post = n++;
pre = ++n;

對於 post = n++; 這個語句,n 的值被賦予 post 後,n 才增 1。也就是說,這個語句執行完後,post 的值是 1,而 n 的值變成 2。而 pre = ++n; 這個語句,n 先增 1,然後再把自增後的值賦予 pre。也就是說,這個語句執行完後,pre 的值是 3,n 的值也是 3。

由此可得,如果 ++ 前置,則 ++ 的運算元先增 1,然後再參與其它運算;如果 ++ 後置,則 ++ 的運算元先參與其它運算,然後才增 1。嚴格地說,前置 ++ 的運算元的值在被使用之前增 1,而後置 ++ 的運算元的值在被使用之後增 1。例如:

int n = 5, post = 1, pre = 1;
pre = ++n + pre; // 運算結束後 pre 為 7
n = 5;
post = n++ + post; // 運算結束後 post 為 6

2. 自減運算子(Decrement Operator)

自減運算子 -- 使運算元的值減 1。-- 可以置於運算元前面,也可以放在後面。例如:

--n ;
n-- ;

自減運算子和自增運算子非常相似,區別只在於自減運算子使運算元減 1,而自增運算子使運算元增 1。例如:

int n = 5, post = 1, pre = 1;
pre = --n + pre; // 運算結束後 pre 為 5
n = 5;
post = n-- + post; // 運算結束後 post 為 6

3. 優先順序

自增運算子和自減運算子的優先順序很高,只有圓括弧的優先順序比它們高。因此,n*m++; 表示 n*(m++); 而不是 (n * m)++; 。而且 (n * m)++; 是錯誤的。因為 ++ 和 -- 的運算元只能是可變左值(modifiable lvalue),而 n * m 不是。

注意,不要把優先順序和取值順序混淆了。例如:

int x = 1, y = 2, z;

z = (x + y++) * 3; // 運算結束後 z 為 9,y 為 3

用數字代替上面的語句得:

z = (1 + 2) * 3;

僅當 y 的值被使用後,y 才會增 1。優先順序表明的是 ++ 僅作用於 y,而不是 (x + y)。優先順序也表明 y 的值何時被使用,但是 y 的值何時增 1 是由自增運算子的本質決定的。

當 y++ 是某個算術運算式的一部分時,您可以認為它表示“先使用 y 的值,然後自增”。類似地,++y 表示“先自增,然後使用自增後的值”。

==========================================================================

以下內容引自《C 語言常見問題集》 原著:Steve Summit 翻譯:朱群英, 孫 雲

http://c-faq-chn.sourceforge.net/ccfaq/index.html

http://www.eskimo.com/~scs/C-faq/top.html

==========================================================================

4.3 對於代碼 int i = 3; i = i++; 不同編譯器給出不同的結果, 有的為 3, 有的為 4, 哪個是正確的?

沒有正確答案;這個運算式無定義。參見問題 3.1, 3.7 和 11.32。 同時注意, i++ 和 ++i 都不同於 i+1。如果你要使 i 自增 1, 使用 i=i+1, i+=1, i++ 或 ++i, 而不是任何組合, 參見問題 3.10。

12.35 有人說 i = i++ 的行為是未定義的, 但是我剛在一個相容 ANSI 的編譯器上測試, 得到了我希望的結果。

面對未定義行為的時候, 包括範圍內的實現定義行為和未確定行為, 編譯器可以做任何實現, 其中也包括你所有期望的結果。但是依靠這個實現卻不明智。參加問題 7.4, 11.31, 11.32 和 11.34。

4.2 使用我的編譯器,下面的代碼 int i=7; printf("%d\n", i++ * i++); 返回 49?不管按什麼順序計算, 難道不該列印出56嗎?

儘管尾碼自加和尾碼自減操作符 ++ 和 -- 在輸出其舊值之後才會執行運算, 但這裡的``之後"常常被誤解。沒有任何保證確保自增或自減會在輸出變數原值之後和對錶達式的其它部分進行計算之前立即進行。也不能保證變數的更新會在運算式 ``完成" (按照 ANSI C 的術語, 在下一個 ``序列點" 之前, 參見問題 3.7) 之前的某個時刻進行。本例中, 編譯器選擇使用變數的舊值相乘以後再對二者進行自增運算。

包含多個不確定的副作用的代碼的行為總是被認為未定義。(簡單而言, ``多個不確定副作用" 是指在同一個運算式中使用導致同一對象修改兩次或修改以後又被引用的自增, 自減和賦值操作符的任何組合。這是一個粗略的定義; 嚴格的定義參見問題 3.7, ``未定義" 的含義參見問題 11.32。) 甚至都不要試圖探究這些東西在你的編譯器中是如何?的 (這與許多 C 教科書上的弱智練習正好相反); 正如 K&R 明智地指出, ``如果你不知道它們在不同的機器上如何?, 這樣的無知可能恰恰會有助於保護你。

4.7 我怎樣才能理解複雜運算式?``序列點" 是什嗎?

序列點是一個時間點(在整個運算式全部計算完畢之後或在 ||、 &&、 ? : 或逗號 運算子處, 或在函數調用之前), 此刻塵埃落定, 所有的副作用都已確保結束。 ANSI/ISO C 標準這樣描述:

在上一個和下一個序列點之間, 一個對象所儲存的值至多隻能被運算式的計算修改一次。而且前一個值只能用於決定將要儲存的值。

第二句話比較費解。它說在一個運算式中如果某個對象需要寫入, 則在同一運算式中對該對象的訪問應該只局限於直接用於計算將要寫入的值。這條規則有效地限制了只有能確保在修改之前才訪問變數的運算式為合法。例如 i = i+1 合法, 而 a[i] = i++ 則非法 (參見問題 3.1)。

參見下邊的問題 3.8。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.