演算子の働きから 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言語は演算子ファーストな言語 と考えて,いろいろ考察してみました.当たり前ですが,ちゃんと理にかなった処理になっていましたね.