演算子

C言語には演算子がたくさんあり、全部で50種類以上あり、いくつかのカテゴリに分類できます。

算術演算子

算術演算子は四則演算に特化して使用されるもので、主に以下の種類があります。

  • +: 正の値演算子(単項演算子)
  • -: 負の値の演算子(単項演算子)
  • +: 加算演算子(二項演算子)
  • -: 減算演算子(二項演算子)
  • *: 乗算演算子
  • /: 除算演算子
  • %: 剰余演算子

(1)+-

+- は単項演算子と二項演算子の両方として使用できます。いわゆる「単項演算子」とは、オペランドを 1 つだけ使用して実行できることを意味します。単項演算子 - は、値の符号を変更するために使用されます。

int x = -12;

上の例では、「-」は値「12」を「-12」に変更します。

単項演算子 + は正の値にも負の値にも影響しません。完全に省略できる演算子ですが、記述してもエラーは報告されません。

int x = -12;
int y = +x;

上記の例では、「+」は正負の値を変更しないため、変数「y」の値は「-12」のままです。

二項演算子「+」と「-」は加算と減算を実行するために使用されます。

int x = 4 + 22;
int y = 61 - 23;

(2)*

演算子 * は乗算を完了するために使用されます。

int num = 5;
printf("%i\n", num * num); // 出力 25

(3)/

演算子 / は除算を完了するために使用されます。 2 つの整数を除算しても、結果は依然として整数であることに注意してください。

浮動小数点数 x = 6 / 4;
printf("%f\n", x); // 1.000000 を出力します。

上記の例では、変数「x」の型は「float」(浮動小数点数)ですが、「6 / 4」の結果は「1.5」ではなく「1.0」になります。その理由は、C言語の整数除算は整数部分だけを返し、小数部分を切り捨てる整数除算だからです。

浮動小数点の結果を取得したい場合は、2 つのオペランドに少なくとも 1 つの浮動小数点数が必要です。その場合、C 言語は浮動小数点除算を実行します。

float x = 6.0 / 4; // または 6 / 4.0 として記述されます。
printf("%f\n", x); // 1.500000 を出力します。

上記の例では、「6.0 / 4」は浮動小数点除算を行うことを意味し、結果は「1.5」になります。

別の例を示します。

int スコア = 5;
スコア = (スコア / 20) * 100;

上記のコードでは、計算後の score25 になると思われるかもしれませんが、実際には score0 になります。これは、「スコア / 20」は均等に割り切れて整数値「0」が得られるため、「100」を掛けても「0」が得られるためです。

期待どおりの結果を得るには、整数の除算が浮動小数点の除算になるように、除数「20」を「20.0」に変更します。

スコア = (スコア / 20.0) * 100;

(4)%

演算子 % はモジュロ演算を表し、2 つの整数を除算した余りを返します。この演算子は整数でのみ使用でき、浮動小数点数では使用できません。

int x = 6 % 4;

モジュロ負数の規則は、結果の符号が最初のオペランドの符号によって決定されるというものです。

11% -5 // 1
-11 % -5 // -1
-11% 5 // -1

上の例では、最初のオペランドの符号 (「11」または「-11」) によって結果の符号が決まります。

(5) 代入演算の短縮形

変数がそれ自体の値に対して算術演算を実行する場合、C 言語では、代入演算子と算術演算子を 1 つの演算子に結合できる短縮形式が提供されます。

  • +=
  • -=
  • *=
  • /=
  • %=

以下にいくつかの例を示します。

i += 3; // i = i + 3 と同等
i -= 8; // i = i - 8 と同等
i *= 9; // i = i * 9 と同等
i /= 2; // i = i / 2 と同等
i %= 5; // i = i % 5 と同等

インクリメント演算子、デクリメント演算子

C 言語には、変数自体に対して「+ 1」および「- 1」演算を実行する 2 つの演算子が用意されています。

  • ++: インクリメント演算子
  • --: デクリメント演算子
i++; // i = i + 1 と同等
i--; // i = i - 1 と同等

これら 2 つの演算子は変数の前または後に配置され、結果は異なります。 ++var--var は最初にインクリメントまたはデクリメント演算を実行し、次に演算の後に var の値を返します。 var++var-- は最初に var の値を返します。値を入力し、インクリメントまたはデクリメント操作を実行します。

int i = 42;
int j;

j = (i++ + 10);
// 私: 43
// j: 52

j = (++i + 10)
// 私: 44
// j: 54

上の例では、インクリメント演算子の位置の違いにより、変数 j が異なる値を取得します。この書き方では予期しない結果が生じやすいため、代わりに次の書き方を使用できます。

/* 書き方1 */
j = (i + 10);
i++;

/* 書き方2 */
i++;
j = (i + 10);

上記の例では、変数 'i' のインクリメント操作と戻り値が 2 つの別々のステップであるため、エラーが発生する可能性が低くなり、コードの可読性が向上します。

関係演算子

C言語では比較に使用する式を「関係式」と呼び、その中で使用される演算子を「関係演算子」と呼びます。 主に以下の6つがあります。

  • > より大きい演算子
  • < より小さい演算子
  • >= 以上の演算子
  • <= 以下演算子
  • == 等価演算子
  • != 不等号演算子

以下にいくつかの例を示します。

a == b;
!= b;
a < b;
a > b;
a <= b;
a >= b;

関係式は通常、true または false を示す「0」または「1」を返します。 C 言語では、「0」は false を意味し、ゼロ以外の値はすべて true を意味します。たとえば、「20 > 12」は「1」を返し、「12 > 20」は「0」を返します。

関係式は、「if」または「while」構造でよく使用されます。

if (x == 3) {
  printf("x は 3.\n");
}

等価演算子 == と代入演算子 = は 2 つの異なる演算子であるため、混同しないように注意してください。場合によっては、次のコードを誤って作成して実行することもありますが、予期しない結果が生じる可能性があります。

if (x = 3) ...

上の例では、本来の意味は「x == 3」ですが、誤って「x = 3」と表記してしまいました。この式は、変数「x」に「3」を代入することを意味し、戻り値は「3」なので、「if」の判定は常に真となります。

この種のエラーを防ぐために、等号の右側に変数を記述することを好むプログラマもいます。

if (3 == x) ...

この場合、== を誤って = と書くと、コンパイラはエラーを報告します。

/* エラーを報告します */
if (3 = x) ...

避けるべきもう 1 つの間違いは、複数の関係演算子を一緒に使用することです。

i < j < k

上の例では、2 つの小なり演算子が連続して使用されています。これは正当な式であり、エラーは報告されませんが、通常は望ましい結果が得られません。つまり、変数 j の値が ik の間にあることは保証されません。関係演算子は左から右に評価されるため、実際には次の式が実行されます。

(i < j) < k

上の式では、「i < j」は「0」または「1」を返すため、最終的には「0」または「1」が変数「k」と比較されます。変数 j の値が i と k の間にあるかどうかを判定したい場合は、次のような書き方をします。

i < j && j < k

論理演算子

論理演算子は論理的な判断機能を提供し、より複雑な式を構築するために使用されます。演算子は主に次の 3 つです。

  • !: 演算子なし (単一の式の真偽を変更します)。
  • &&: AND 演算子 (両側の式が true の場合は true、そうでない場合は false)。
  • ||: OR 演算子 (両側の少なくとも 1 つの式が true の場合は true、そうでない場合は false)。

以下は AND 演算子の例です。

if (x < 10 && y > 20)
  printf("何かをしています!\n");

上の例では、「x < 10 && y > 20」は、「x < 10」と「y > 20」が同時に真である場合にのみ真になります。

以下は if 演算子の例です。

if (!(x < 12))
  printf("x は 12 以上です\n");

上の例では、否定演算子 !< よりも優先順位が高いため、式 x < 12 を否定するには括弧を使用する必要があります。もちろん、これを記述する適切な方法は「if (x >= 12)」ですが、これは単なる例です。

論理演算子の場合、ゼロ以外の値は true を表し、ゼロ値は false を表します。たとえば、「5 || 0」は「1」を返し、「5 && 0」は「0」を返します。

論理演算子のもう 1 つの特徴は、常に左側の式が最初に評価され、次に右側の式が評価されることです。この順序が保証されます。左側の式が論理演算子の条件を満たす場合、右側の式は評価されなくなります。この状況を「短絡」と呼びます。

if (数値 != 0 && 12/数値 == 2)

上記の例では、&& の左側の式 (number != 0) が false の場合、つまり number0 に等しい場合、右側の式 (12) は、 /number == 2) は実装されません。この時点で左側の式は 0 を返すため、&& 式全体が false になる必要があるため、直接 0 を返し、右側の式は実行されなくなります。

論理演算子の実行順序は最初に左、次に右であるため、次のコードには問題があります。

while ((x++ < 10) && (x + y < 20))

上の例では、左側の式を実行した後、変数 x の値が変更されています。右側の式が実行されるまでに、新しい値が評価に使用されますが、これは通常、本来の意図ではありません。

ビット演算子

C 言語には、バイナリ ビットを操作するためのビット単位の演算子がいくつか用意されています。

(1) 否定演算子 ~

否定演算子 ~ は単項演算子で、各 2 進ビットを反対の値に変更するために使用されます。つまり、「0」が「1」になり、「1」が「0」になります。

//01101100を返す10010011

上の例では、「~」は各バイナリ ビットを反転して新しい値を取得します。

~ 演算子は変数の値を変更せず、新しい値を返すだけであることに注意してください。

(2) AND演算子「&」

AND 演算子「&」は、2 つの値の各 2 進数を比較し、新しい値を返します。両方の 2 進ビットが「1」の場合は「1」を返し、それ以外の場合は「0」を返します。

//00010001を返す
10010011 & 00111101

上の例では、2 つの 8 ビット 2 進数がビットごとに比較され、新しい値が返されます。

AND 演算子「&」は代入演算子「=」と組み合わせることができ、「&=」と省略されます。

int val = 3;
val = val&0377;

// と省略します
val &= 0377;

(3) または演算子 |

OR 演算子 | は 2 つの値の各 2 進数を比較し、新しい値を返します。 2 つのバイナリ ビットのうちの 1 つが「1」である限り (両方が「1」である場合を含む)、「1」が返され、そうでない場合は「0」が返されます。

// 10111111 を返す
10010011 | 00111101

OR 演算子 | は代入演算子 = と組み合わせることができ、これは |= と省略されます。

int val = 3;
値 = 値 0377;

// と省略します
val |= 0377;

(4) XOR 演算子 ^

排他的 OR 演算子 ^ は、2 つの値の各 2 進数を比較し、新しい値を返します。 2 つのバイナリ ビットのうち 1 つだけが「1」の場合は「1」を返し、それ以外の場合は「0」を返します。

//10101110を返す
10010011^00111101

排他的 OR 演算子 ^ は代入演算子 = と組み合わせることができ、^= と省略されます。

int val = 3;
val = val ^ 0377;

// と省略します
val ^= 0377;

(5) 左シフト演算子「<<」

左シフト演算子 << は、左オペランドの各ビットを指定された桁数だけ左に移動し、最後の空の位置を 0 で埋めます。

// 1000101000
10001010 << 2

上の例では、「10001010」の各 2 進ビットが左に 2 桁シフトされます。

左シフト演算子は、指定された 2 の累乗でオペランドを乗算することと同等です。たとえば、オペランドを 2 ビット左にシフトすることは、4 (2 の 2 乗) を乗算することと同等です。

左シフト演算子「<<」は代入演算子「=」と組み合わせることができ、「<<=」と省略されます。

int val = 1;
val = val << 2;

// と省略します
val <<= 2;

(6) 右シフト演算子 >>

右シフト演算子 >> は、左オペランドの各ビットを指定された桁数だけ右に移動します。末尾に収まらない値は破棄され、先頭の空いた位置が埋められます。 「0」。

//00100010を返す
10001010 >>2

上の例では、「10001010」の各 2 進ビットが右に 2 桁シフトされます。下2桁の「10」は切り捨てられ、先頭の余分な2桁は「0」で埋められるので、最終的には「00100010」になります。

右シフト演算子は、符号なし整数でのみ使用するのが最適で、負の数では使用しないことに注意してください。システムが異なれば、右シフト後の負の数の符号ビットの処理方法も異なるため、異なる結果が得られる場合があります。

右シフト演算子は、指定された 2 の累乗でオペランドを除算することと同等です。たとえば、オペランドを 2 ビット右にシフトすることは、4 (2 の 2 乗) で除算することと同等です。

右シフト演算子 >> は代入演算子 = と組み合わせることができ、>>= と省略されます。

int val = 1;
val = val >> 2;

// と省略します
val >>= 2;

カンマ演算子

カンマ演算子は、複数の式を一緒に記述し、各式を左から右に順番に実行するために使用されます。

x = 10、y = 20;

上の例では、2 つの式 (x = 10y = 20) があり、コンマを使用することでそれらを同じステートメント内に配置できます。

カンマ演算子は、最後の式の値をステートメント全体の値として返します。

int x;

x = (1, 2, 3);

上の例では、括弧内のカンマ演算子は最後の式の値を返すため、変数 x3 と等しくなります。

操作の優先順位

優先順位とは、式に複数の演算子が含まれている場合に、どの演算子を最初に実行するかを指します。さまざまな演算子の優先順位は異なります。

3 + 4 * 5;

上の例では、式 3 + 4 * 5 には加算演算子 (+) と乗算演算子 (*) の両方が含まれています。加算よりも乗算の方が優先されるため、「3 + 4」ではなく「4 * 5」が最初に計算されます。

2 つの演算子の優先順位が同じ場合、実行順序は、演算子が左結合か右結合かに基づいて決定されます。ほとんどの演算子は左結合 (左から右に実行) ですが、代入演算子 (=) などのいくつかの演算子は右結合 (右から左に実行) です。

5*6/2;

上の例では、*/ は同じ優先順位を持ち、どちらも左結合演算子であるため、最初に 5 * 6 が計算され、次に 30 / 2 が計算されます。計算された。

演算子の優先順位は複雑です。以下に、いくつかの演算子の優先順位を示します (優先順位の高いものから低いものの順に並べています)。

  • 括弧 (())
  • インクリメント演算子 (++)、デクリメント演算子 (--)
  • 単項演算子 (+ および -)
  • 掛け算(*)、割り算(/)
  • 加算 (+)、減算 (-)
  • 関係演算子 (<> など)
  • 代入演算子(=)

括弧の優先順位が最も高いため、括弧を使用して他の演算子の優先順位を変更できます。

int x = (3 + 4) * 5;

上の例では、括弧の追加により、乗算の前に加算が実行されます。

すべての演算子の優先順位を完全に覚えておく必要はありません。解決策は、より多くの括弧を使用して、予期しない状況を防ぎ、コードの読みやすさを向上させることです。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0