カンマ演算子 , は何をするのでしょうか?
-
09-06-2019 - |
質問
は何ですか ,
演算子はCで行うのですか?
解決
表現:
(expression1, expression2)
最初に式 1 が評価され、次に式 2 が評価され、式全体に対して式 2 の値が返されます。
他のヒント
最もよく使われているのを見たことがあります while
ループ:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
操作を実行してから、副作用に基づいてテストを実行します。他の方法は次のようにすることです。
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
の カンマ演算子 左のオペランドを評価し、結果を破棄してから右のオペランドを評価すると、それが結果となります。の 慣用的な リンクに記載されているように、使用は、で使用される変数を初期化するときです。 for
ループすると、次の例が表示されます。
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
そうでなければ多くはありません 素晴らしい の用途 カンマ演算子, 、そうですが 悪用しやすい 読みにくく保守しにくいコードを生成します。
から C99標準のドラフト 文法は次のとおりです。
expression:
assignment-expression
expression , assignment-expression
そして 段落2 言います:
の カンマ演算子の左オペランドは void 式として評価されます。 評価後にシーケンスポイントがあります。そうして 右オペランドが評価されます。結果にはその型と値があります。 97) カンマ演算子の結果を変更しようとしたり、次のシーケンス ポイントの後にアクセスしようとした場合の動作は未定義です。
脚注97 言います:
カンマ演算子は次のことを行います 左辺値を生成しない.
つまり、結果に代入することはできません。 カンマ演算子.
カンマ演算子には次のものがあることに注意することが重要です。 最も低い優先順位 したがって、使用する場合があります ()
たとえば、次のように大きな違いを生む可能性があります。
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
次の出力が得られます。
1 4
カンマ演算子は、その両側にある 2 つの式を 1 つに結合し、両方を左から右の順序で評価します。右辺の値が式全体の値として返されます。 (expr1, expr2)
のようなものです { expr1; expr2; }
しかし、次の結果を使用できます expr2
関数呼び出しまたは代入で。
でよく見られます for
ループを使用して、次のように複数の変数を初期化または維持します。
for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
/* do something with low and high and put new values
in newlow and newhigh */
}
これとは別に、私はマクロで常に一緒に行う必要がある 2 つの操作をまとめるとき、他の 1 つのケースで「怒りで」これを使用しただけです。ネットワーク上に送信するためにさまざまなバイナリ値をバイト バッファにコピーするコードがあり、次の作業を行った場所にポインタが維持されていました。
unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;
*ptr++ = first_byte_value;
*ptr++ = second_byte_value;
send_buff(outbuff, (int)(ptr - outbuff));
値があった場所 short
または int
私たちはこれを行いました:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
後で、これが実際には有効な C ではないことが分かりました。 (short *)ptr
は左辺値ではなくなったためインクリメントできませんが、当時のコンパイラは気にしませんでした。これを修正するには、式を 2 つに分割します。
*(short *)ptr = short_value;
ptr += sizeof(short);
ただし、このアプローチは、すべての開発者が常に両方のステートメントを忘れずに入れる必要がありました。出力ポインター、値、および値の型を渡すことができる関数が必要でした。これはテンプレートを使用した C++ ではなく C であるため、関数に任意の型を取ることができなかったため、マクロを使用することにしました。
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
カンマ演算子を使用することで、これを式またはステートメントとして希望どおりに使用できます。
if (need_to_output_short)
ASSIGN_INCR(ptr, short_value, short);
latest_pos = ASSIGN_INCR(ptr, int_value, int);
send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));
これらの例のどれもが良いスタイルであると言っているわけではありません。確かに、私はスティーブ・マコーネルのことを覚えているようです コードの完成 コンマ演算子を使用しないことをお勧めします。 for
ループ:読みやすさと保守性を高めるために、ループは 1 つの変数と、 for
行自体にはループ制御コードのみを含める必要があり、その他の初期化やループ メンテナンスの追加部分は含めないでください。
複数のステートメントが評価されますが、結果の値 (rvalue だと思います) として最後のステートメントのみが使用されます。
それで...
int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() );
x は 8 に設定されるはずです。
カンマ演算子は何の意味も持たず、100% 余分な機能です。これの主な用途は「賢くなろうとする人々」であり、したがって、可読コードを (意図せずに) 難読化するために使用されます。主な使用分野は、for ループを難読化することです。次に例を示します。
for(int i=0, count=0; i<x; i++, count++)
どこ int i=0, count=0
は実際にはカンマ演算子ではなく、宣言リストです (ここですでに混乱しています)。 i++, count++
はコンマ演算子で、最初に左のオペランドを評価し、次に右のオペランドを評価します。カンマ演算子の結果は、右側のオペランドの結果になります。左のオペランドの結果は破棄されます。
ただし、上記のコードは、カンマ演算子を使用しないと、はるかに読みやすい方法で作成できます。
int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
...
count++;
}
私が見たカンマ演算子の唯一の実際の用途は、シーケンス ポイントに関する人為的な議論です。カンマ演算子には、左オペランドと右オペランドの評価の間にシーケンス ポイントが付属するからです。
したがって、次のような未定義の動作コードがある場合:
printf("%d %d", i++, i++);
実際には、次のように記述することで、単に不特定の動作 (関数パラメータの評価順序) に変えることができます。
printf("%d %d", (0,i++), (0,i++));
の各評価間にシーケンス ポイントが存在します。 i++
, したがって、関数パラメータの評価順序が指定されていないとしても、少なくともプログラムがクラッシュしたり燃えたりする危険はなくなります。
もちろん、実際のアプリケーションでそのようなコードを書く人は誰もいないでしょう。これは、C 言語のシーケンス ポイントに関する言語弁護士の議論にのみ役立ちます。
カンマ演算子は、読みにくいコードを作成するという理由で、MISRA-C:2004 および MISRA-C:2012 によって禁止されています。
以前の回答で述べたように、すべてのステートメントを評価しますが、最後のステートメントを式の値として使用します。個人的には、これがループ式でのみ役立つと感じました。
for (tmp=0, i = MAX; i > 0; i--)
これが便利だと私が見た唯一の場所は、式の 1 つ (おそらく init 式またはループ式) で複数の処理を実行するファンキーなループを作成する場合です。何かのようなもの:
bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
size_t i1, i2;
for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
{
if(a1[i1] != a2[i2])
{
return false;
}
}
return true;
}
構文エラーがある場合、または厳密な C 以外のものが混入している場合はご容赦ください。, 演算子が良い形式であると主張するわけではありませんが、それを使用できるのです。上記の場合、おそらく使用します while
代わりにループを実行することで、init とloop の複数の式がより明確になります。(そして、宣言してから初期化するのではなく、i1 と i2 をインラインで初期化します。何とか何とか何とか。)
@Rajesh と @JeffMercado からの質問に対処するためにこれを復活させます。これは検索エンジンでヒットするトップの 1 つであるため、非常に重要だと思います。
たとえば、次のコードのスニペットを見てみましょう
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
印刷されます
1 5
の i
このケースは、ほとんどの回答で説明されているように処理されます。すべての式は左から右の順序で評価されますが、最後の式のみが割り当てられます。 i
. 。の結果 (
表現 )is
1`.
の j
ケースは異なる優先規則に従います。 ,
演算子の優先順位が最も低くなります。これらのルールにより、コンパイラは次のように認識します。 代入式、定数、定数 .... 。式は再び左から右の順序で評価され、その副作用は表示されたままとなるため、次のようになります。 j
は 5
の結果として j = 5
.
興味深いことに、 int j = 5,4,3,2,1;
言語仕様では許可されていません。アン イニシャライザ を期待しています 代入式 だから直接 ,
演算子は許可されません。
お役に立てれば。