演算子の働きから C言語の理解を深めようというお話です.
演算子とは
そもそも,演算子ってなんでしょう?
例えば, 5 + 2
であれば, 5 と 2 の和を求める計算をします.
なので,引数を 2つ持ち,戻り値がある関数を考えてもよいですね.
上記の例では plus( 5, 2)
という感じでしょうか.
C言語の関数であれば int plus(int, int)
のように,引数や戻り値に型を指定する必要があります.ですが,演算子は整数でも実数でも受け付けてくれます.
ですので,ちょっと言い過ぎかもしれませんが,
演算子は,オーバーロード機能のある組み込み関数
と言えると思います.
C言語の演算子
C言語にはどのような演算子があるでしょう?
望洋先生のサイト がとても参考になります.といいますか,このページから今回のネタを思いつきました.
関数呼出し演算子
望洋先生の演算子一覧表を見ると,のっけからハードな演算子があります.
関数の引数を入れる ()
って演算子だったんですね.
つまり, func(x)
は, func
と x
の 2項演算ということになります.func
を関数ポインタであると考えると,そんなに違和感ない考え方ですね.
func(x,y)
の場合, func
と x,y
の 2項演算で,さらに x,y
の ,
はコンマ演算子・・・となるとカッコいいのですが,ここでの ,
は単なるセパレータとなってしまうが,ちょっと残念な感じです.
それはともかく,関数呼び出しも演算子なので, C言語は演算子ファーストな言語 って言ってもよいかなと思います.
添字演算子
次に,配列の添字の指定をする []
も演算子なんですね.
これは, a[1]
と *(a + 1)
が同じ処理であることから,素直に納得できます.
ところで, x + y
は y + x
としても同じ結果になります. +
は可換性を持つとも言います. x % y
には可換性はないですね.
では, []
には可換性があるのでしょうか?
やってみると分かりますが, a[1] = 2;
と 1[a] = 2;
は,同じ処理になります.可換性があるんですね.
a[1]
と *(a + 1)
が同じ処理で,かつ, +
に可換性があるのだから, []
も可換性を持つのは理にかなっています.
単純代入演算子
代入 =
も演算子なんですね.
x = y
としたとき, x
に y
の値を代入し,演算結果(戻り値)は y の値になります.
この x = y
を subst(x, y)
という関数に置き換えて考えてみます.厳密には subst(&x,y)
が正しいと思いますが,表記を簡単にするために x
でいきます.
もう一度,望洋先生の演算子一覧表を見ると,右端に結合性という項目があります.
x + y
は,「2項+演算子」で,結合性は「左」です.これは x + y + z
のように 2項演算+ が連続する場合には,左から結合していくという意味です.ですので (x + y) + z
のように,左にある x + y
を先に処理することになります.
x + y
を plus(x,y)
という関数で置き換えたとすると, x + y + z
は plus( plus(x,y), z)
と同じになります.
では,単純代入演算子はどうかというと,結合性は「右」です.
ですので, x = y = z
は subst(x, subst(y, z))
になります.
もう分かりましたね.単純代入演算子の結合性を「右」にしておくと,
x = y = z = 1
は
subst( x, subst( y, subst( z, 1) ) )
となり, subst( z, 1)
の戻り値は 1
になるので
subst( x, subst( y, 1) )
となり・・・で,最終的に x y z 全てに 1 が代入されます.
まとめ
C言語は演算子ファーストな言語 と考えて,いろいろ考察してみました.当たり前ですが,ちゃんと理にかなった処理になっていましたね.