[C言語入門] 演算子の使い方(2)
高木です。おはようございます。
前回に引き続き、「侍エンジニア塾」の「【C言語入門】演算子の使い方一覧(優先順位/余り/インクリメント)」にインスパイアされた記事の続きを書くことにします。
「全然入門記事じゃない」という声も聞こえてきそうですが、気にせずどんどん進めていきましょう。
前回は本当にざっと演算子の一覧を挙げただけでしたので、今回はその中の「後置演算子」に絞って詳しく解説していきます。
必要に応じて、C言語の入門書を見直すようにすると理解が深まると思いますよ。
本文中、サンプルコードをいくつか掲載します。
ポイントを絞ったコードになっていますので、実際にコンパイルして実行するにはmain関数を補うなどしなければなりませんのでご注意ください。
後置演算子
C言語の後置演算子は、オペランドの後ろに演算子を記述する一種の単項の演算子です。
加減乗除を行う演算子に比べて後置演算子はかなり特殊ですので、最初は戸惑うかもしれません。
しかし、いい加減な比喩などに頼ることなく、ひとつひとつを正確に理解していけばそんなに難しいものではありません。
添字演算子
添字演算子はその名前の通り、配列の要素に添字を使ってアクセスするための演算子です。
ただし、正確なことをいうと、添字演算子にはポインタ型と整数型のオペランドが必要になります。
左右どちらがポインタ型で、どちらが整数型でもかまいません。
たとえば、E1[E2]
という式があったとしましょう。
添字演算子が使われていますね。
上の式は、(*((E1) + (E2)))
と同じ意味であると定義されています。
このことから、int a[10];
という配列に対して、a[5]
のように要素を取り出せるように、5[a]
としても同じことができます。
ただし、実際にこんな書き方をすることはネタ以外ではほとんどありません。
E1またはE2の一方がポインタ型でもう一方が整数型でなければならないのですが、配列型を添字演算子のオペランドにした場合には、暗黙的に配列の先頭要素へのポインタに型変換されます。
1 2 3 4 | int array[10]; int x ; x = array[5]; /* arrayはarray[0]へのポインタ型に暗黙的に変換される。 */ |
連続した添字演算子を使えば、多次元配列の要素にアクセスすることもできます。
これについては、また別の機会に詳しく解説できればと考えています。
関数呼出演算子
関数を呼び出すための丸括弧もC言語では演算子の扱いになります。
関数呼出演算子のオペランドは、関数へのポインタ型でなければなりません。
関数型ではなく「関数へのポインタ型」が必要です。
ただし、関数名を指定した場合は暗黙的にその関数へのポインタ型に型変換されるので、関数呼出演算子は期待通りに機能してくれます。
関数呼出演算子の丸括弧の中に、コンマで区切られた式を並べた場合、それらは関数を呼び出す上での実引数になります。
関数を呼び出す前にそれぞれの実引数が評価され、対応する仮引数に代入されます。
関数原型(プロトタイプ)がない場合や、関数原型があっても省略記号(, …)で終わっているために対応する仮引数がない場合には、「既定の実引数拡張」が行われます。
既定の実引数拡張については、別の機会があれば解説したいと考えていますが、現時点で興味がある方は自分で検索してみてください。
構造体および共用体のメンバーへのアクセス
.演算子と矢印(->)演算子は、構造体や共用体のメンバーにアクセスするための演算子です。
一見すると二項演算子のように見えますが、これらの演算子のあとに続くのはメンバーを表す識別子であってオペランドではありません。
.演算子は(修飾版または非修飾版の)構造体型または共用体型がオペランドになるのに対して、矢印演算子は(修飾版または非修飾版の)構造体型または共用体型へのポインタ型がオペランドになる点が異なります。
修飾版または被修飾版というのは、const修飾子やvolatile修飾子が付いていても付いていなくても使えるという意味です。
対象となる構造体や共用体が修飾版、すなわちconstまたはvolatile修飾子が付いている場合、.演算子や矢印演算子の評価結果もやはり同じ修飾子が付くことになります。
1 2 3 4 5 6 7 8 9 10 11 12 | struct A { int x; double y; }; struct A a; a.x = 123; a.y = 4.56; printf("%d %f\n", a.x, a.y); /* 実行結果: 123 4.560000 */ |
後置増分演算子と後置減分演算子
増分演算子と減分演算子は、インクリメント演算子とデクリメント演算子とも呼ばれます。
英語ではpostfix increment operatorとpostfix decrement operatorなので、その音訳ですね。
JISの翻訳では増分演算子と減分演算子となっていますので、ここではそのように呼ぶことにします。
これらの演算子のオペランドは、(修飾版または非修飾版の)実数型またはポインタ型でなければなりません。
「実数型」というのは浮動小数点型のことだと説明している入門サイトなども少なくないようですが、これは間違っています。
「実数型」というのは、整数型と実浮動小数点型(float型、double型、long double型)の総称です。
C言語には虚数を表す複数型がありますので、それに対しての実数型なのです。
ちなみに、「浮動小数点型」といった場合は実浮動小数点型と複素数型の総称になります。
また、これらの演算子のオペランドは変更可能な左辺値でなければなりません。
定数とかconst修飾されているオブジェクトとかはダメなのです。
ですので、修飾版の場合はvolatile修飾子またはrestrict修飾子で修飾されたものだけがOKになります。
これらの演算子の評価結果は、もとのオペランドの値となります。
しかし、これらの演算子を評価することで、オペランドに格納されている値が変化します。
後置増分演算子の場合はもとの値に1を足した結果が、後置減分演算子の場合は1を引いた結果が格納されます。
1 2 3 4 5 6 7 8 | int a = 1; printf("%d\n", a++); printf("%d\n", a); /* 実行結果 1 2 */ |
実際にオペランドの値が更新されるタイミングは、直前の副作用完了点から次の副作用完了点までの間のどこかです。
副作用完了点(sequence point)についての詳細はここでは割愛します。
興味のある方は是非ご自分で調べてみてください。