わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Cの糖衣構文について

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いきながら,経験を積みたいものです.

*1:あくまで例であり,この例に関しては,breakをなくすことは絶対に勧められません.

*2:辞書を引いたところ,nestも「温床」の意味を持つのですね.まあ,プログラミングではnestと言えば「入れ子」ですが.

*3:本を読むだけではダメ.自分でプログラムを書くだけではダメ.教師・上司から指導を受け,良いも悪いも含め,人のコードも読んでいくことが欠かせません.