C语言学习及应用笔记之一:C运算符优先级及使用问题
C語言中的運算符絕對是C語言學習和使用的一個難點,因為在2011版的標準中,C語言的運算符的數量超過40個,甚至比關鍵字的數量還要多。這些運算符有單目運算符、雙目運算符以及三目運算符,又涉及到左結合和右結合的問題,真是令人眼花繚亂。
1、運算符及優先級
運算符多可能使用更靈活方便,但這還涉及到運算符之間的優先級問題。我們做四則運算式時,有先乘除后加減的規定,在C語言的這些運算符中自然也是有的,但40多個運算符排起優先級來,使用就不那么容易了。
接下來我們簡單的總結一下C語言中運算符以及他們的優先級。C語言中各運算符的優先級如下表所示:
| 優先級 | 運算符 | 名稱或含義 | 使用形式 | 結合方向 | 說明 |
| 1 | [] | 數組下標 | 數組名[常量表達式] | 左到右 | ? |
| () | 圓括號 | (表達式)/函數名(形參表) | ? | ||
| . | 成員選擇(對象) | 對象.成員名 | ? | ||
| -> | 成員選擇(指針) | 對象指針->成員名 | ? | ||
| ++ | 后置自增運算符 | ++變量名 | 單目運算符 | ||
| -- | 后置自減運算符 | --變量名 | 單目運算符 | ||
| 2 | - | 負號運算符 | -表達式 | 右到左 | 單目運算符 |
| (類型) | 強制類型轉換 | (數據類型)表達式 | ? | ||
| ++ | 前置自增運算符 | 變量名++ | 單目運算符 | ||
| -- | 前置自減運算符 | 變量名-- | 單目運算符 | ||
| * | 取值運算符 | *指針變量 | 單目運算符 | ||
| & | 取地址運算符 | &變量名 | 單目運算符 | ||
| ! | 邏輯非運算符 | !表達式 | 單目運算符 | ||
| ~ | 按位取反運算符 | ~表達式 | 單目運算符 | ||
| sizeof | 長度運算符 | sizeof(表達式) | ? | ||
| 3 | / | 除 | 表達式/表達式 | 左到右 | 雙目運算符 |
| * | 乘 | 表達式*表達式 | 雙目運算符 | ||
| % | 余數(取模) | 整型表達式/整型表達式 | 雙目運算符 | ||
| 4 | + | 加 | 表達式+表達式 | 左到右 | 雙目運算符 |
| - | 減 | 表達式-表達式 | 雙目運算符 | ||
| 5 | << | 左移 | 變量<<表達式 | 左到右 | 雙目運算符 |
| >> | 右移 | 變量>>表達式 | 雙目運算符 | ||
| 6 | > | 大于 | 表達式>表達式 | 左到右 | 雙目運算符 |
| >= | 大于等于 | 表達式>=表達式 | 雙目運算符 | ||
| < | 小于 | 表達式<表達式 | 雙目運算符 | ||
| <= | 小于等于 | 表達式<=表達式 | 雙目運算符 | ||
| 7 | == | 等于 | 表達式==表達式 | 左到右 | 雙目運算符 |
| != | 不等于 | 表達式!= 表達式 | 雙目運算符 | ||
| 8 | & | 按位與 | 表達式&表達式 | 左到右 | 雙目運算符 |
| 9 | ^ | 按位異或 | 表達式^表達式 | 左到右 | 雙目運算符 |
| 10 | | | 按位或 | 表達式|表達式 | 左到右 | 雙目運算符 |
| 11 | && | 邏輯與 | 表達式&&表達式 | 左到右 | 雙目運算符 |
| 12 | || | 邏輯或 | 表達式||表達式 | 左到右 | 雙目運算符 |
| 13 | ?: | 條件運算符 | 表達式1? 表達式2: 表達式3 | 右到左 | 三目運算符 |
| 14 | = | 賦值運算符 | 變量=表達式 | 右到左 | ? |
| /= | 除后賦值 | 變量/=表達式 | ? | ||
| *= | 乘后賦值 | 變量*=表達式 | ? | ||
| %= | 取模后賦值 | 變量%=表達式 | ? | ||
| += | 加后賦值 | 變量+=表達式 | ? | ||
| -= | 減后賦值 | 變量-=表達式 | ? | ||
| <<= | 左移后賦值 | 變量<<=表達式 | ? | ||
| >>= | 右移后賦值 | 變量>>=表達式 | ? | ||
| &= | 按位與后賦值 | 變量&=表達式 | ? | ||
| ^= | 按位異或后賦值 | 變量^=表達式 | ? | ||
| |= | 按位或后賦值 | 變量|=表達式 | ? | ||
| 15 | , | 逗號運算符 | 表達式,表達式,… | 左到右 | 從左向右順序運算 |
在上表中,我們歸納了運算符、個運算符的功能、通常的應用表達式形式以及結合性。說到結合性主要應用于相同優先級的運算符,運算次序由結合方向所決定。絕大部分的運算符都是左結合的,與我們的常識一致。不過有一部分運算符是右結合的,這些就需要記憶了,但記憶有時候卻不見得百分百準確,所以我們可以在編寫代碼時以( )加以規范。
2、優先級的一些特別說明
我們見面歸納了運算符的用法,但這只是一般性情況,實際的使用情況有時候依然讓人迷惑。比方說,在上表中,如果優先級同為1 的幾種運算符同時出現時,想要確定表達式的優先級就不是那么明顯了。例如我們常常對指向多維數組的指針和指針數組,還有指向函數的指針數組等含混不清,對初學者來說就更是迷惑不解。所以在這樣的定義表達式中,如果優先級不明確結果有可能相去甚遠。在這里,接下來我們整理了一些常常容易出錯的情況說明如下:
| 優先級問題 | 表達式 | 想要的結果 | 實際結果 |
| .的優先級高于* | *p.f | 指針p所指向的對象的某一字段f,即:(*p).f | 對p取f偏移作為指針,然后進行解除飲用操作,即:*(p.f) |
| []的優先級高于* | int *p[] | P是指向int數組的指針,即:int (*p)[] | P是元素為指向int的指針的數組,即:int *(p[]) |
| 函數()優先級高于* | int *p() | p是個函數指針所指函數返回值是int,即:int (*p)() | P是個函數,返回值是int *,即:int *(p()) |
| ==和!=優先級高于位操作 | (val&mask!=0) | (val&mask)!=0 | val&(mask!=0) |
| ==和!=優先級高于賦值操作 | c=getchar()!=EOF | (c=getchar())!=EOF | c=(getchar()!=EOF) |
| 算術運算符高于移位運算符 | msb<<4+lsb | (msb<<4)+lsb | msb<<(4+lsb) |
| 逗號運算符的優先級最低 | i=1,2 | i=(1,2) | (i=1),2 |
那么是不是有了上面的總結就完事大吉了呢?當然不可能,且不說我們的總結很不完備,就算你記住了所有的規則,在使用過程中仍然有可能漏洞百出,最好的辦法是養成良好的編碼習慣。
3、由優先級引發的錯誤
優先級問題經常會造成一些意想不到的結果,有的甚至是非常嚴重的。本人在編寫程序時,就出現過一些因為疏忽運算符優先級造成的問題,而且檢查起來非常不容易發現。
例如,有一次,我們采集了傳感器的原始數據,然后會對數據進行一些處理,在其中的一種條件下會對一個數進行左移幾位并加上一個數。由于算術運算符的優先級大于移位運算符,而程序員忘記了給移位操作加括號,所以得出了結果總是有誤,只好從頭開始查找,花了不少時間才發現這出錯誤。如msb<<4+lsb和(msb<<4)+lsb是完全不一樣,因為算術運算符的優先級大于移位運算符。
還有一次,我們定義一個指向函數的指針數組用于回調函數的操作。定義時,沒有考慮到指針運算符、函數運算符以及數組運算符的優先級問題而造成調用出錯。如,void (*p[])f()的形式,如果寫成void *p[]f()的形式就完全錯誤了。
當然,如果能夠熟記各種運算符的優先級也許能解決上面的問題,但這實際上卻很難達到,因為應用是非常靈活的,你不可能時時刻刻只關注于此。我覺得養成良好的編碼習慣以及多多練習似乎更有效。
歡迎關注:
總結
以上是生活随笔為你收集整理的C语言学习及应用笔记之一:C运算符优先级及使用问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奇妙的安全旅行之加密算法概述
- 下一篇: mybatis调用oracle过程,使用