CとC++の演算子

CとC++の演算子(シーとシープラスプラスのえんざんし)はC++C言語における演算子の一覧である。C++に存在するすべての演算子を掲示し、さらにCにも存在するかどうかを示している。なお、Cでは演算子の多重定義は不可能である。

&&||?:条件演算子)ならびに,コンマ演算子)は副作用完了点英語版を形成する。ただし、多重定義されていない場合に限る。

これらのうち、Cにも存在するものは、コンマ演算子と矢印演算子を除いて、JavaPerlC#PHPにも同じ優先順位・結合性を持って存在する。ただし、PHPの条件演算子は例外的に左から右への結合である。

演算子の表

この表においてa, b, cは有効な値 (リテラル・値・変数・戻り値)・オブジェクト名・左辺値を適宜意味する。なお、「多重定義可能か」はC++での話である。

CとC++の演算子の表

算術演算子

名称構文多重定義可能かC言語に存在するか
単項プラス+aYesYes
加算a + bYesYes
前置インクリメント++aYesYes
後置インクリメントa++YesYes
加算代入a += bYesYes
単項マイナス(負符号)-aYesYes
減算a - bYesYes
前置デクリメント--aYesYes
後置デクリメントa--YesYes
減算代入a -= bYesYes
乗算a * bYesYes
乗算代入a *= bYesYes
除算a / bYesYes
除算代入a /= bYesYes
剰余a % bYesYes
剰余代入a %= bYesYes

比較演算子

名称構文多重定義可能かC言語に存在するか
小なりa < bYesYes
小なりイコールa <= bYesYes
大なりa > bYesYes
大なりイコールa >= bYesYes
非等価a != bYesYes
等価a == bYesYes

論理演算子

名称構文多重定義可能かC言語に存在するか
論理否定!aYesYes
論理積a && bYesYes
論理和a || bYesYes

ビット演算子

名称構文多重定義可能かC言語に存在するか
左シフトa << bYesYes
左シフト代入a <<= bYesYes
右シフトa >> bYesYes
右シフト代入a >>= bYesYes
ビット否定~aYesYes
ビット積a & bYesYes
ビット積代入a &= bYesYes
ビット和a | bYesYes
ビット和代入a |= bYesYes
ビット排他的論理和a ^ bYesYes
ビット排他的論理和代入a ^= bYesYes

型変換演算子

名称構文多重定義可能かC言語に存在するか
動的キャストdynamic_cast<type>(a)NoNo
静的キャストstatic_cast<type>(a)NoNo
定置性キャストconst_cast<type>(a)NoNo
強制キャストreinterpret_cast<type>(a)NoNo
型変換(キャスト)(type)aYesYes
型変換(関数記法)type(a)YesNo

その他の演算子

名称構文多重定義可能かC言語に存在するか
単純代入a = bYesYes
関数呼出a()YesYes
配列添え字a[b]YesYes
間接演算子 (デリファレンス)*aYesYes
アドレス&aYesYes
間接メンバa->bYesYes
直接メンバa.bNoYes
間接メンバポインタa->*bYesNo
直接メンバポインタa.*bNoNo
コンマ演算子a , bYesYes
条件演算子a ? b : cNoYes
スコープ解決英語版a::bNoNo
メンバへのポインタa::*bNoNo
アライメント
(C11/C++11)
alignof(type)NoYes
sizeofsizeof a
sizeof(type)
NoYes
型情報typeid(a)
typeid(type)
NoNo
領域確保new typeYesNo
領域確保(配列)new type[n]YesNo
領域解放delete aYesNo
領域解放(配列)delete[] aYesNo

演算子の優先順位

以下の表は、C++とCにおける優先順位と結合性を示したものである(なお、Java, Perl, PHPなど最近の言語の多くは同様の優先順位を持つ)。演算子は優先順位の低いものほど下のほうに掲載されている。同じセルに掲載されている演算子同士は同じ優先度を持つ。なお、たとえ多重定義しても、演算子の優先順位は変化しない。

CやC++において、演算子の構文は文脈自由文法にて定義されている。この表は、その構文より導き出されるものである。

この表では、正確に表現できていない部分が一部に存在する。たとえば、条件演算子は、代入やコンマ演算子より高い優先度を持つものの、中央のオペランドではそれらを含むすべての演算子の使用が可能である。すなわち、a ? b , c : da ? (b, c) : dと解釈される。また、(a ? b), (c : d)は意味を成さず、コンパイルエラーとなる。なお、括弧で囲われていない型変換式の結果はsizeofの対象となれない。つまり、sizeof (int) * x(sizeof(int)) * xの意味となりsizeof ((int) * x)とはならない。

演算子名称結合性
::スコープ解決 (C++のみ)左から右
++ --後置インクリメント・デクリメント
()関数呼出し
[]配列添え字
.直接メンバアクセス
->間接メンバアクセス
typeid()実行時型情報 (C++のみ)
const_cast型変換 (C++のみ)
dynamic_cast型変換 (C++のみ)
reinterpret_cast型変換 (C++のみ)
static_cast型変換 (C++のみ)
++ --前置インクリメント・デクリメント右から左
+ -単項プラスとマイナス
! ~論理否定とビット否定
(type)型変換
*間接演算子 (デリファレンス)
&アドレス
sizeof記憶量
new new[]動的記憶域確保 (C++のみ)
delete delete[]動的記憶域解放 (C++のみ)
.* ->*メンバへのポインタ (C++のみ)左から右
* / %乗算・除算・剰余算
+ -加算・減算
<< >>左シフト・右シフト
< <=関係演算子)小なり・小なりイコール
> >=大なり・大なりイコール
== !=等価・非等価
&ビット積
^ビット排他的論理和
|ビット和
&&論理積
||論理和
c ? t : f条件演算子右から左
(throwは結合しない)
=単純代入
+= -=加算代入・減算代入
*= /= %=乗算代入・除算代入・剰余代入
<<= >>=左シフト代入・右シフト代入
&= ^= |=ビット積代入・ビット排他的論理和代入・ビット和代入
throw送出代入 (例外送出: C++のみ)
,コンマ演算子左から右

補足

優先順位の表は、括弧で括られていない式において結びつく順序を決める。

  • ++x * 3は優先順位の規則がなければ曖昧である。しかし、実際には、優先順位によってx*より++に結びつくので、(++x) * 3と解釈される。
  • 同様に、3 * x++ではxのみがインクリメントの対象となる。
  • 優先順位と結合性の問題は上記のダイアグラム[要追加記述]のように一般化できる。コンパイラの目標はこのようなダイアグラムを式として解決することである。この図は各単項の演算子(ここではそれぞれ3 + ( . ), 2 * ( . ), ( . )++, ( . )[i]と表記する)がyへ結合しようとしていることを表す。優先順位の表から、各部分式は最終的に、( . )[i]yへ、( . )++y[i]へ、2 * ( . )(y[i])++へ、3 + ( . )2 * ((y[i])++)という結合にしか成り得ないことが決定される。
    • 注意: 優先順位からは部分式が「何」に対して作用するかを決めるが、「いつ」作用するかには関与しない。上の2 * ((y[i])++)という例では、優先順位と直接には関係なく、単に演算のためにその値が必要であるという(データフロー的な)理由により、後置++演算子がy[i]より後に作用する、ということしか明確ではない。

CやC++において、演算子の結合は、優先順位ではなく(各々の標準規格での)文法によって定められている。このため、僅かな差異が生じる場合がある。たとえば、Cの条件演算子は以下のように定義されている。

logical-OR-expression ? expression : conditional-expression

一方、C++では次のように定義されている。

logical-or-expression ? expression : assignment-expression

そのため、

e = a ? b : c = d

という式は、Cだと

e = ((a ? b : c) = d)

と解釈されて条件演算子の結果が左辺値でないことによるエラーとなるが、C++だと

e = (a ? b : (c = d))

と解釈され、正しい式となる。

論理演算子の優先順位は問題があると指摘されている[1]。概念的には&|は算術演算子の+-のような存在である。しかし、a + b == 7(a + b) == 7と解釈されるが、a & b == 7という式はa & (b == 7)と解析されてしまう。ほかにも期待通りに解釈させるための括弧を必要とする場合がある。

C++の演算子の代替表現

C++では、一部の演算子に対して、全く同じに機能する代替表現を予約している[1]。代替表現はキーワードと同様に予約されている字句として扱われる。

代替表現対応する演算子
and&&
bitand&
and_eq&=
or||
bitor|
or_eq|=
xor^
xor_eq^=
not!
not_eq!=
compl~

これらの代替表現は、記号的に等価として扱われ、どこでも記号を代替表現に置き換えても問題ない。置き換える先は、演算子としてではなく記号として扱われる。つまりbitandはビット積演算子だけでなくアドレス取得演算子の代わりに用いても機能するということである。

ANSI Cでは、これらを<iso646.h>でCプリプロセッサのマクロを用いて定義している。これとの互換性のために、C++ではヘッダ<iso646.h>と<ciso646>を用意しているが、中身は空である。

C++では、すべての比較演算子は真偽値を返す。

bool a;int b = 1;int c = 2;a = b == c;

==はbool型の値を返すため、==演算子はif文などの中に限定されず、どこでも使用可能である。なお、a = b == c;は次のように書くのと同様の効果をもたらす。

bool a;int b = intに変換できる任意の値;int c = もう一つの任意値;if (b == c)    a = true;else    a = false;

脚注

関連項目