二元運算子 . 和 -> 常常被稱為點運算子(dot operator)和
箭頭運算子(arrow operator),借助於這兩個運算子,可以選擇結構或聯合中的成員。
例 1 展示了點運算子的左運算元必須是一個結構或者一個聯合,而右運算元必須是該型別(結構或聯合)成員的名字。
【例1】
struct Article { long number; // 物品編號
char name[32]; // 物品名字
long price; // 物品單價(精確到美分)
/* ... */
};
struct Article sw = { 102030L, "Heroes", 5995L };
sw.price = 4995L; // 將價格改為49.95
點運算結果的型別,與所選擇成員的型別是一樣的。如果左運算元是一個左值,那麼該運算也會產生左值。如果左運算元的型別有限定符(例如被宣告為 const),那麼結果型別也有該限定符。
點運算子的左運算元並非一定是左值,如下例所示:
struct Article getArticle(); // 函數原型
printf( "name: %sn", getArticle().name );
函數 getArticle()返回一個 struct Article 型別的物件。按此結果,getArticle().name 是一個有效的表示式,但不是一個左值,因為函數的返回值不是一個左值。
運算子 -> 也可用於選擇結構或聯合的成員,但是
箭頭運算子的左運算元必須是一個指標,它指向一個結構或聯合型別。
右運算元是該結構或聯合成員的名字。例 2 展示了運算子->的用法,同樣使用例 1 所定義的結構 Article。
【例2】
struct Article *pArticle = &sw, // 一個指向struct Article的指標
const *pcArticle = &sw; // 一個指向struct Article的唯讀指標
++(pArticle->number); // 增加編號
if ( pcArticle->number == 102031L ) // 正確:獲取唯讀指標
pcArticle->price += 50; // 錯誤:不能使用限定符const的指標來修改物件
箭頭運算子的結果總是一個左值。它具有被選取成員的型別,也同樣包括了其指標運算元的任何型別限定符。在例 2 中,pcArticle 是一個指向 const struct Article 的指標。其結果是,表示式 pcArticle->price 是一個常數。
包含箭頭運算子的任何表示式,都可以利用點運算子進行重寫,做法是先將指標解參考,然後使用點運算子:表示式 p->m 等效於(*p).m;相反地,如果 x 是左值的話,表示式 x.m 等效於(&x)->m。
和運算子 [] 一樣,點運算子 . 和箭頭運算子 -> 都具有最高的優先順序,並且組合方式都是從左到右。因此,表示式 ++p->m 等同於 ++(p->m),表示式 p->m++ 等同於(p->m)++。
然而,表示式(*p).m 中的括號是有必要的,因為復參照運算子 * 的優先順序比較低。表示式 *p.m 等效於 *(p.m),這種等效僅當在成員 m 是指標時才有意義。
我們通過結合下標運算子、點運算子和箭頭運算子,對一個元素為結構的陣列進行操作,來總結本文講述的問題:
struct Article arrArticle[10]; // 一個具有10個元素的陣列
// 每個元素為結構型別
arrArticle[2].price = 990L; // 設定陣列元素arrArticle[2]的成員price
arrArticle->number = 10100L; // 設定陣列元素arrArticle[0]的成員number
一個陣列名稱,例如本例中的 arrArticle,是一個指向第一個陣列元素的常數指標。所以 arrArticle->number 指向第一個陣列元素的成員 number。簡單地說,對於任一的索引值 i,下面 3 個表示式是等價的:
arrArticle[i].number
(arrArticle+i)->number
(*(arrArticle+i)).number
它們都指向陣列中索引值為 i 的元素的成員 number。