CおよびC++プログラミング言語では、コンマ演算子(トークン で表される,) は、最初のオペランドを評価して結果を破棄し、次に 2 番目のオペランドを評価してこの値 (および型) を返すバイナリ演算子です。これらの評価の間にはシーケンス ポイントがあります。
演算子としてのコンマ トークンの使用は、関数呼び出しや定義、変数宣言、列挙型宣言、および同様の構造での区切り文字としての使用とは異なります。
構文
カンマ演算子は、セミコロンが文を終了させるのと似た方法で、値を持つ式を区切ります。また、文のシーケンスが中括弧で囲まれるのと似た方法で、式のシーケンスが括弧で囲まれます。 [1] はカンマで区切られた式のシーケンスで、最後の式に評価されますが、 は文のシーケンスで、値には評価されません。カンマは 2 つの式の間にのみ使用できます (カンマは式を区切ります)。これは、(非ブロック) 文の末尾に出現するセミコロンとは異なり、セミコロンは文を終了します。
(a, b, c)c{a; b; c;}
カンマ演算子は C 演算子の中で最も優先順位が低く、シーケンス ポイントとして機能します。カンマとセミコロンの組み合わせでは、セミコロンはカンマよりも優先順位が低くなります。これは、セミコロンが文を区切るのに対し、カンマは文の中に出現するためです。これは、通常の句読点としての使用と一致します。これらは 2 つの別々の文であるため、
a, b; c, dとしてグループ化されます。(a, b); (c, d)
カンマ演算子は、式を添え字に使うときには非推奨となりました( C++20以降)。[2]これは混乱を減らし、将来的に多次元配列のインデックス付けの構文を再利用できるようにするためです。C ++23では、複数の引数でオーバーロードする機能が追加されたため、括弧で囲まれていないカンマ式は添え字に使えなくなりました。 [3]カンマ式が括弧で囲まれている場合( など)は、このコンテキストでもカンマ演算子は使用可能であり、非推奨ではありません。
operator[]a[(b,c)]
例
この例では、2 行目と 3 行目の動作が異なるのは、コンマ演算子の優先順位が代入演算子よりも低いためです。最後の例も、関数が戻る前に return 式を完全に評価する必要があるため、動作が異なります。
/**
* この行では、カンマは演算子ではなく区切り文字として機能します。
* 結果: a=1、b=2、c=3、i=0
*/
int a = 1 、b = 2 、c = 3 、i = 0 ;
/**
* b の値を i に代入します。
* カンマは最初の行では区切り文字として、2 行目では演算子として機能します。
* 結果: a=1, b=2, c=3, i=2
*/
int a = 1 , b = 2 , c = 3 ; int i = ( a , b ); /** * a の値を i に代入します。* 以下と同等: int i = a; int b; * カンマは両方の行で区切り文字として機能します。* 2 行目の中括弧は、同じブロック内での変数の再宣言を回避します。再宣言すると、コンパイル エラーが発生します。* 2 番目に宣言された b には初期値が与えられません。* 結果: a=1, b=2, c=3, i=1 */ int a = 1 , b = 2 , c = 3 ; { int i = a , b ; }
/**
* a の値を 2 増やし、結果の演算 a + b の値を i に代入します。
* カンマは、最初の行では区切り文字として、2 行目では演算子として機能します。
* 結果: a=3、b=2、c=3、i=5
*/
int a = 1 、b = 2 、c = 3 ; int i = ( a += 2 、a + b ); /** * a の値を 2 増やし、a の値を i に格納し、結果の演算 a + b の未使用の値を破棄します。* 次の式と同等: (i = (a += 2)), a + b; * カンマは、最初の行では区切り文字として、3 行目では演算子として機能します。* 結果: a=3、b=2、c=3、i=3 */ int a = 1 、b = 2 、c = 3 ; int i ; i = a += 2 、a + b ;
/**
* a の値を i に代入します。
* 両方の行で、カンマは区切り文字として機能します。
* 2 行目の中括弧は、同じブロック内での変数の再宣言を回避します。
これにより、コンパイル エラーが発生します。
* 2 番目に宣言された b と c には初期値は与えられません。
* 結果: a=1、b=2、c=3、i=1
*/
int a = 1 、b = 2 、c = 3 ; { int i = a 、b 、c ; }
/**
* カンマは最初の行では区切り文字として機能し、2 行目では演算子として機能します。
* 未使用の a および b の値を破棄して、c の値を i に割り当てます。
* 結果: a=1、b=2、c=3、i=3
*/
int a = 1 、b = 2 、c = 3 ; int i = ( a 、b 、c );
/** *
キーワード return に続くカンマ演算子シーケンス ポイントは、最終
部分式 c=6の右辺値に評価される単一の式と見なされるため、4 ではなく 6 を返します。
* この行では、カンマは演算子として機能します。*/ return a = 4 , b = 5 , c = 6 ;
/**
* 前の例と同じ理由で、1 ではなく 3 を返します。
* この行では、カンマは演算子として機能します。
*/
return 1 , 2 , 3 ;
/**
* 上記と同じ理由で、1 ではなく 3 を返します。この例がこのように機能するのは、
* return がキーワードであり、関数呼び出しではないためです。コンパイラーは
return(value) 構造を許可しますが、括弧は "value" に対してのみ相対的であり
、 * return キーワードに特別な効果はありません。
* return は単に式を取得し、ここでの式は "(1), 2, 3" です。
* この行では、カンマは演算子として機能します。
*/
return ( 1 ), 2 , 3 ;
用途
カンマ演算子の使用例は比較的限られています。最初のオペランドを破棄するため、通常は最初のオペランドに望ましい副作用があり、その副作用を 2 番目のオペランドの前に並べる必要がある場合にのみ役立ちます。さらに、特定の慣用句以外で使用されることはめったになく、他のカンマやセミコロンと間違えやすいため、混乱を招きやすく、エラーが発生しやすくなります。それでも、for ループやSFINAEなど、一般的に使用される特定の状況があります。[4]デバッグ機能が限られている可能性のある組み込みシステムでは、カンマ演算子をマクロと組み合わせて使用することで、関数呼び出しをシームレスにオーバーライドし、関数呼び出しの直前にコードを挿入できます。
ループ
最も一般的な使用法は、主にfor ループの初期化式と増分式で、ブロック ステートメントを使用せずに複数の代入ステートメントを許可することです。これは、初歩的な C プログラミングにおける唯一の慣用的な使用法です。次の例では、ループの初期化子の順序が重要です。
void rev ( char * s , size_t len ) { char * first ; for ( first = s , s += len ; s >= first ; -- s ) { putchar ( * s ); } }
他の言語では、この問題に対する代替解決策として並列代入があります。並列代入では、1つの文の中で複数の代入が可能で、構文と意味は異なりますが、コンマも使用できます。Goでは、類似のforループでこれが使用されています。 [5]
for ループ初期化子 (セミコロンの特別な用途がある) 以外では、特に問題のステートメントがループ増分と同様に機能する場合 (while ループの終了時など)、セミコロンの代わりにコンマが使用されることがあります。
++ p 、++ q ; ++ p ; ++ q ;
マクロ
プリプロセッサ マクロでコンマを使用すると、単一の構文式のスペースで複数の操作を実行できます。
一般的な使用法の 1 つは、失敗したアサーションでカスタム エラー メッセージを提供することです。これは、かっこで囲まれた式リストをassertマクロに渡すことによって行われます。最初の式はエラー文字列で、2 番目の式はアサートされる条件です。マクロassertは、アサーションが失敗すると、引数をそのまま出力します。次に例を示します。
#include <stdio.h> #include <assert.h>
int main ( void ) { int i ; for ( i = 0 ; i <= 9 ; i ++ ) { assert ( ( "i は大きすぎます!" , i <= 4 ) ); printf ( "i = %i \n " , i ); } return 0 ; }
出力:
私 = 0 私 = 1 私 = 2 私 = 3 私 = 4 アサート: assert.c:6: test_assert: アサーション `( "i は大きすぎます!", i <= 4 )' が失敗しました。 中止
ただし、assert マクロは通常、製品コードでは無効になっているため、デバッグ目的でのみ使用してください。
状態
カンマは条件内(if、while、do while、または for)で使用して、ブロックスコープで関数を呼び出してその結果を使用する補助的な計算を許可できます。
if ( y = f ( x ), y > x ) { ... // x と y を含むステートメント}
Goにも同様の慣用句があり、if文の構文では明示的にオプションの文が許可されています。[6]
複雑なリターン
カンマは return ステートメントで使用して、グローバル変数または出力パラメータ (参照渡し) に割り当てることができます。この慣用句は、割り当てが実際の return で終了するブロック内の補助的な割り当てではなく、戻り値の一部であることを示しています。たとえば、グローバル エラー番号を設定する場合:
失敗の場合、errno = EINVAL 、-1を返します。
これをより詳細に記述すると、次のようになります。
if (失敗) { errno = EINVAL ; return -1 ; }
ブロックを避ける
簡潔にするために、次のようにコンマを使用してブロックと関連する中括弧を省略できます。
( x == 1 )の場合、y = 2 、z = 3 ;
( x == 1 )の場合、y = 2 、z = 3 ;
の代わりに:
x == 1の場合、y = 2 ; z = 3 ; }
x == 1の場合、y = 2 ; z = 3 ; }
その他の言語
OCamlとRubyプログラミング言語では、セミコロン(「;」)がこの目的で使用されます。JavaScript [7]とPerl [8]は、C / C ++と同じようにコンマ演算子を使用します。Javaでは、コンマはさまざまなコンテキストでリスト内の要素を区切るために使用される区切り文字です。[9]これは演算子ではなく、リストの最後の要素に評価されません。[10]
参照
参考文献
- ^ 「カンマ演算子」。Microsoft開発者ドキュメント。2019 年 8 月 2 日のオリジナルからアーカイブ。2019年8 月 1 日に取得。
カンマで区切られた 2 つの式は、左から右に評価されます。左のオペランドは常に評価され、すべての副作用は右のオペランドが評価される前に完了します。
- ^ 「P1161R2: 式を添え字付けする際のコンマ演算子の使用は非推奨」www.open-std.org . 2022年9月5日閲覧。
- ^ マーク・ホエメン;デイジー・ホールマン。コランタン・ジャボ。イザベラ・ムエルテ。クリスチャン・トロット (2021-09-14)。 「多次元添字演算子」(PDF)。
- ^ “SFINAE”. en.cppreference.com . 2022年8月30日閲覧。
- ^ Effective Go: for、「最後に、Go にはカンマ演算子がなく、++ と -- は式ではなくステートメントです。したがって、for で複数の変数を実行する場合は、並列割り当てを使用する必要があります (ただし、++ と -- は使用できません)。」
- ^ Go プログラミング言語仕様: if 文
- ^ 「カンマ演算子」。MDN Web Docs . 2020年1月17日。2014年7月12日時点のオリジナルよりアーカイブ。2020年1月25日閲覧。
単一の式が必要な場所に複数の式を含めたい場合は、カンマ演算子を使用できます。
- ^ 「Perlop - Perl の演算子と優先順位 - Perldoc ブラウザ」。
- ^ “2.4. 文法表記法”. Oracle Corporation . 2019年7月22日時点のオリジナルよりアーカイブ。2019年7月25日閲覧。
- ^ 「Java では、コンマ (,) は演算子ですか、それとも区切り文字ですか?」
文献
- Ramajaran, V. (1994)、Computer Programming in C、ニューデリー: Prentice Hall of India
- ディクシット、JB (2005)、コンピュータの基礎とC言語プログラミング、ニューデリー:Laxmi Publications
- カーニハン、ブライアン W.、リッチー、デニス M. (1988)、『プログラミング言語 C』(第 2 版)、ニュージャージー州エングルウッド クリフス:プレンティス ホール
外部リンク
- 「C および C++ でセミコロンの代わりにコンマを使用した場合の効果」、Stack Overflow
- Boost.Assignment –コンマ演算子をオーバーロードすることでコンテナを簡単に埋めることができるBoostライブラリの機能
