「CとC++の演算子」の版間の差分
m 曖昧さ回避ページへのリンクを修正 タグ: 2017年版ソースエディター |
|||
338行目: | 338行目: | ||
CやC++において、演算子の結合は、優先順位ではなく(各々の標準規格での)文法によって定められている。このため、僅かな差異が生じる場合がある。たとえば、Cの条件演算子は以下のように定義されている。 |
CやC++において、演算子の結合は、優先順位ではなく(各々の標準規格での)文法によって定められている。このため、僅かな差異が生じる場合がある。たとえば、Cの条件演算子は以下のように定義されている。 |
||
< |
<syntaxhighlight lang="c"> |
||
logical-OR-expression ? expression : conditional-expression |
logical-OR-expression ? expression : conditional-expression |
||
</syntaxhighlight> |
|||
</source> |
|||
一方、C++では次のように定義されている。 |
一方、C++では次のように定義されている。 |
||
< |
<syntaxhighlight lang="c++"> |
||
logical-or-expression ? expression : assignment-expression |
logical-or-expression ? expression : assignment-expression |
||
</syntaxhighlight> |
|||
</source> |
|||
そのため、 |
そのため、 |
||
<pre>e = a ? b : c = d</pre> |
<pre>e = a ? b : c = d</pre> |
||
という式は、Cだと |
という式は、Cだと |
||
< |
<syntaxhighlight lang="c"> |
||
e = ((a ? b : c) = d) |
e = ((a ? b : c) = d) |
||
</syntaxhighlight> |
|||
</source> |
|||
と解釈されて条件演算子の結果が左辺値でないことによるエラーとなるが、C++だと |
と解釈されて条件演算子の結果が左辺値でないことによるエラーとなるが、C++だと |
||
< |
<syntaxhighlight lang="cpp"> |
||
e = (a ? b : (c = d)) |
e = (a ? b : (c = d)) |
||
</syntaxhighlight> |
|||
</source> |
|||
と解釈され、正しい式となる。 |
と解釈され、正しい式となる。 |
||
404行目: | 404行目: | ||
C++では、すべての比較演算子は真偽値を返す。 |
C++では、すべての比較演算子は真偽値を返す。 |
||
< |
<syntaxhighlight lang="cpp"> |
||
bool a; |
bool a; |
||
int b = 1; |
int b = 1; |
||
int c = 2; |
int c = 2; |
||
a = b == c; |
a = b == c; |
||
</syntaxhighlight> |
|||
</source> |
|||
<code>==</code>はbool型の値を返すため、<code>==</code>演算子はif文などの中に限定されず、どこでも使用可能である。なお、<code>a = b == c;</code>は次のように書くのと同様の効果をもたらす。 |
<code>==</code>はbool型の値を返すため、<code>==</code>演算子はif文などの中に限定されず、どこでも使用可能である。なお、<code>a = b == c;</code>は次のように書くのと同様の効果をもたらす。 |
||
< |
<syntaxhighlight lang="cpp"> |
||
bool a; |
bool a; |
||
int b = intに変換できる任意の値; |
int b = intに変換できる任意の値; |
||
419行目: | 419行目: | ||
else |
else |
||
a = false; |
a = false; |
||
</syntaxhighlight> |
|||
</source> |
|||
== 脚注 == |
== 脚注 == |
2020年7月5日 (日) 23:01時点における版
本項はC++とC言語における演算子の一覧である。C++に存在するすべての演算子を掲示し、さらにCにも存在するかどうかを示している。なお、Cでは演算子の多重定義は不可能である。
&&
、||
、?:
(条件演算子)ならびに,
(コンマ演算子)は副作用完了点を形成する。ただし、多重定義されていない場合に限る。
これらのうち、Cにも存在するものは、コンマ演算子と矢印演算子を除いて、Java、Perl、C#、PHPにも同じ優先順位・結合性を持って存在する。ただし、PHPの条件演算子は例外的に左から右への結合である。
演算子の表
この表においてa
, b
, c
は有効な値 (リテラル・値・変数・戻り値)・オブジェクト名・左辺値を適宜意味する。なお、「多重定義可能か」はC++での話である。
算術演算子 | |||
名称 | 構文 | 多重定義可能か | C言語に存在するか |
---|---|---|---|
単項プラス | +a |
Yes | Yes |
加算 | a + b |
Yes | Yes |
前置インクリメント | ++a |
Yes | Yes |
後置インクリメント | a++ |
Yes | Yes |
加算代入 | a += b |
Yes | Yes |
単項マイナス(負符号) | -a |
Yes | Yes |
減算 | a - b |
Yes | Yes |
前置デクリメント | --a |
Yes | Yes |
後置デクリメント | a-- |
Yes | Yes |
減算代入 | a -= b |
Yes | Yes |
乗算 | a * b |
Yes | Yes |
乗算代入 | a *= b |
Yes | Yes |
除算 | a / b |
Yes | Yes |
除算代入 | a /= b |
Yes | Yes |
剰余 | a % b |
Yes | Yes |
剰余代入 | a %= b |
Yes | Yes |
比較演算子 | |||
名称 | 構文 | 多重定義可能か | C言語に存在するか |
小なり | a < b |
Yes | Yes |
小なりイコール | a <= b |
Yes | Yes |
大なり | a > b |
Yes | Yes |
大なりイコール | a >= b |
Yes | Yes |
非等価 | a != b |
Yes | Yes |
等価 | a == b |
Yes | Yes |
論理演算子 | |||
名称 | 構文 | 多重定義可能か | C言語に存在するか |
論理否定 | !a |
Yes | Yes |
論理積 | a && b |
Yes | Yes |
論理和 | a || b |
Yes | Yes |
ビット演算子 | |||
名称 | 構文 | 多重定義可能か | C言語に存在するか |
左シフト | a << b |
Yes | Yes |
左シフト代入 | a <<= b |
Yes | Yes |
右シフト | a >> b |
Yes | Yes |
右シフト代入 | a >>= b |
Yes | Yes |
ビット否定 | ~a |
Yes | Yes |
ビット積 | a & b |
Yes | Yes |
ビット積代入 | a &= b |
Yes | Yes |
ビット和 | a | b |
Yes | Yes |
ビット和代入 | a |= b |
Yes | Yes |
ビット排他的論理和 | a ^ b |
Yes | Yes |
ビット排他的論理和代入 | a ^= b |
Yes | Yes |
型変換演算子 | |||
名称 | 構文 | 多重定義可能か | C言語に存在するか |
動的キャスト | dynamic_cast<type>(a) |
No | No |
静的キャスト | static_cast<type>(a) |
No | No |
定置性キャスト | const_cast<type>(a) |
No | No |
強制キャスト | reinterpret_cast<type>(a) |
No | No |
型変換(キャスト) | (type)a |
Yes | Yes |
型変換(関数記法) | type(a) |
Yes | No |
その他の演算子 | |||
名称 | 構文 | 多重定義可能か | C言語に存在するか |
単純代入 | a = b |
Yes | Yes |
関数呼出 | a() |
Yes | Yes |
配列添え字 | a[b] |
Yes | Yes |
間接演算子 (デリファレンス) | *a |
Yes | Yes |
アドレス | &a |
Yes | Yes |
間接メンバ | a->b |
Yes | Yes |
直接メンバ | a.b |
No | Yes |
間接メンバポインタ | a->*b |
Yes | No |
直接メンバポインタ | a.*b |
No | No |
コンマ演算子 | a , b |
Yes | Yes |
条件演算子 | a ? b : c |
No | Yes |
スコープ解決 | a::b |
No | No |
メンバへのポインタ | a::*b |
No | No |
アライメント (C11/C++11) |
alignof(type) |
No | Yes |
sizeof | sizeof a sizeof(type) |
No | Yes |
型情報 | typeid(a) typeid(type) |
No | No |
領域確保 | new type |
Yes | No |
領域確保(配列) | new type[n] |
Yes | No |
領域解放 | delete a |
Yes | No |
領域解放(配列) | delete[] a |
Yes | No |
演算子の優先順位
以下の表は、C++とCにおける優先順位と結合性を示したものである(なお、Java, Perl, PHPなど最近の言語の多くは同様の優先順位を持つ)。演算子は優先順位の低いものほど下のほうに掲載されている。同じセルに掲載されている演算子同士は同じ優先度を持つ。なお、たとえ多重定義しても、演算子の優先順位は変化しない。
CやC++において、演算子の構文は文脈自由文法にて定義されている。この表は、その構文より導き出されるものである。
この表では、正確に表現できていない部分が一部に存在する。たとえば、条件演算子は、代入やコンマ演算子より高い優先度を持つものの、中央のオペランドではそれらを含むすべての演算子の使用が可能である。すなわち、a ? b , c : d
はa ? (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;
脚注
- ^ SC22 Committee, The C++ Standards Working group (1998) (PDF). ISO/IEC 14882:1998(E), Programming Languages - C++. International standardization subcommittee for programming languages. pp. 40-41