Cの糖衣構文(シンタックスシュガー)にどれだけあるか,調べてみました.
糖衣構文とその意義については,http://ja.wikipedia.org/wiki/%E7%B3%96%E8%A1%A3%E6%A7%8B%E6%96%87をご覧ください.「配列のアクセス」と「for文」が記載されているので,以下では省いています.
- 複合代入演算子: 「a += b;」と書けば「a = a + b;」を意味する演算子 += がその代表です.ただし,復号代入演算子を使うと,左辺値は一度しか実行されません.「*p++ += *q++;」という式を考えたとき(この式の使い道はよく分かりませんが)は「*p = *p + *q; p++; q++;」と等価です.
- 増分・減分演算子: 「a++;」は「a = a + 1;」です.前置と後置で意味が変わりますが,いずれにせよ,この演算子を使わない式で書き換えられます.ちなみにRubyにはこの演算子がありません(参考1, 参考2).
- アロー演算子: 構造体参照の「->」です.「a->b」は,「(*a).b」のことです.演算子の優先順位に注意すると,「*a.b」とは書けません.
- 三項演算子: 「a = cond ? b : c;」は,「if (cond) { a = b; } else { a = c; }」と等価です.
- 否定演算子: 「a != b」は,「!(a == b)」です.「a > b || a < b」と一緒…と言って,いいのかな.算術型・ポインタ型を考える限りは,3つの式は等価です.
- 文字列の初期化: 「char a[] = "9/6";」と配列変数を宣言すると,要素数をコンパイラが求めてくれて,結局,「char a[4] = {'9', '/', '6', '\0'};」と同じになります.
- switch〜case文: コード例は省略します.if文で表現できますが,一つの整数値に基づく多分岐は,switch〜caseで書くほうが自然でしょう.
- 反復の中のbreak.
while (条件1) { ... if (条件2) break; ... }
これは,フラグ変数を用いて以下のように書けば,breakをなくすことができます*1.
int flag = 1; while (条件1 && flag) { ... if (条件2) flag = 0; else { ... } }
それぞれについて,活用すべきかか,自分の授業や研究室内での指導をもとに,分類してみました.
「積極的に使用」については,使用するほうが字数が減るし,読みやすくなります.
「慎重に使用」については,確かに使うべきところで使うといいのだけど,switch〜caseは,これが入れ子にでもなれば,一つの関数が長大化して読みにくくなること,breakは「どこで反復を終えるか」の判断が複数になり,バグの温床a haven for bugs*2であることに,注意をすべきです.
「基本的に不使用」な三項演算子ですが,字数は減っても,見にくくなります.カッコも増えますし.
ここで,糖衣構文を検討することになったきっかけの文章を引用します.
しかし,なぜポインタを介した構造体のメンバ変数の参照に,わざわざ「->」が必要なのかは私にも理解できない.(略) わざわざ「->を別に規定するより「*」だけを使う方がよほど一貫性があるのではないだろうか.
(ここが変だよC言語 下, p.5)
これを読んだ瞬間に,「シンタックスシュガー」が思い浮かんだのでした.
「一貫性があるのではないだろうか」で主張を終えていますが,文法もさることながら,その使い方…ある処理の書き方が複数あるときに,どれを選ぶのが書きやすく読みやすいか…を理解して*3いきながら,経験を積みたいものです.