2-6 演算子

2-6-1   C# の演算子

  C# の演算子は、C++ とほとんど同じです。以下に、演算子の一覧を示します(表 2-5 )。この一覧から、それぞれ演算子の役割はだいたい理解できるかと思います。以降、2-6-2 「インクリメントとデクリメント」からは、C# の演算子の特徴的な事項について説明します。

●表 2-5 ●  C# の演算子

種類
演算子
四則演算など 足し算 +
引き算 - 掛け算 * 割り算 / 余り %
インクリメント ++
デクリメント --
ビット演算 ビット反転 ~
左シフト << 右シフト >> 論理和 論理積 & 排他的論理和 ^
条件式 等号 ==
不等号 != 大小比較 <、>、<=、>= タイプ識別 変数 is タイプ
条件論理式 かつ &&
または ||
条件オペレータ (条件) ?  値 1 :値 2
代入をともなう演算式 =、+=、-=、*=、/=、%=
&;=、| =、~=、<<=、>>=

 なお、これらの演算子は組み合わせて使うことができます。その場合、演算子には優先順位がありますが、すべての演算子の優先順位を暗記する訳にもいきません。ほかのプログラミング言語と同様に、丸括弧 () で演算の優先順位を指定することができるので、複数の演算子を使うときは、丸括弧 () を使って明示的に優先順位を指定したほうがようでしょう。

[例]優先順位を指定する

  
    x = (y + z) * 100;
  
<ワンポイント>

● for VB6

  C# の演算子は、VB と同じものも多いですが、微妙に違うものもあるので注意が必要です。C# の条件式で使う等号は、「==」というように等号が 2 つ続きます。C# では等号 1 つ「=」は、変数への値の代入を意味し、等しいかどうかの条件評価の意味では使いません。

● for C++

  C# の演算子は、C++ とほとんど同じです。前述の演算子一覧表を見て、その意味が理解できれば、2-6 「演算子」は読みとばしてもよいでしょう。

  1 つ特殊な演算子をあげるとすれば「is」です。これは条件式などで使い、例えば「s is string」というように記述すれば、

「参照型変数 s が参照するオブジェクトは string 型であるか?」

というように、特定の参照先のオブジェクトの「型」を問う比較演算子です。

2-6-2  インクリメントとデクリメント

 インクリメント演算子( ++ )とデクリメント演算子( -- )は、演算項目を 1 つだけとる単項演算子です。以下のように記述することができ、計算対象は 1 つの変数です。それぞれ、対象となる変数に 1 だけ加算、1 だけ減算します。

[例]演算子 ++、演算子 --

  
1:  int i=1, j=10; 
2:  i++; //i は 2 になる(加算)
3:  j--; //j は 9 になる(減算)
4:  --j; //j は 8 になる

 この演算子の特徴として、3 行目、4 行目にあるように、演算子を前後どちらにもつけることができます。上記の例では、3 行目と 4 行目の機能は結果的に違いはありませんが、次の例の 10 行目と 11 行目のように、ほかの演算子とともに計算式で利用されると、計算結果に違いがあります。

[例]計算式の中で、演算子 ++

  
 1:namespace MySpace
 2:{
 3: public class MyApp 
 4: {
 5:  public static void Main(string [] args)
 6:  {
 7:   int i=100, j=100; 
 8:   int c, d;
 9:   string s; 
10:   c = (++i) + 10; //i は 101、計算結果は 111
11:   d = (j++) + 10; //j は 101、計算結果は 110
12:   s = System.String.Format("i={0},j={1}",i,j); 
13:   System.Console.WriteLine(s); 
14:   s = System.String.Format("c={0},d={1}",c,d); 
15:   System.Console.WriteLine(s); 
16:  }
17: }
18:}

(実行結果)

  
    i=101,j=101
c=111,d=110
  

  10 行目と 11 行目の 2 つの式では、++ 演算子の位置が前か後ろかの違いだけで、加算後の変数 i と j の値は同じです。

 両方の式とも、丸括弧 () で ++ 演算子を囲んでいますが、もともと ++ のほうが、後ろの + よりも優先順位は高いので、この丸括弧は省略もできます。両者の計算式とも、まずインクリメント演算子をともなう丸括弧内の処理がされて、その丸括弧内の値と 10 との足し算がおこなわれます。

 しかし、丸括弧の中の ++ 演算子は、変わった処理をします。10 行目のように、先に ++ が書いてある場合、変数の値を先に増加させて、その結果を次の足し算の処理に渡します。つまり、変数 i は 101 になり、その値と 10 との足し算になります。ところが、後に ++ があると、++ 演算子はまず変数の値を、後に続く計算式に渡してから、その後で変数自身の値を増加させます。そのため、変数 j の最初の値である 100 が後に続くべき足し算に回されるので、足し算の結果は 110 になります。もちろんインクリメントもおこなわれるので、変数 j は 101 です。

2-6-3  ビット演算

  2-6-1 「C# の演算子」にあげた C# のビット演算子は、C/C++ のビット演算子と同様の機能があります。

 ビットの反転をおこなう演算子は単項演算子で、対象となる項目の前につけます。

[例]変数 d のビットを反転

  
1: uint d = 0x0000ffff, r; 
2: r = ~d; // 変数 r は、0xffff0000 になる

 「|」や「&」、「^」はビットの基本的な演算をおこなう 2 項演算子で、それぞれ論理和( OR )、論理積( AND )、排他的論理和( XOR )を表します。

[例]変数 a,b の基本的ビット操作

  
1: uint a = 0x00ffffff, c; 
2: uint b = 0xffffff00;
3: c = a | b; //OR c は、0xffffffff
4: c = a & b; //AND c は、0x00ffff00
5: c = a ^ b; //XOR c は、0xff0000ff

 また、「<<」や「>>」はビットをシフトする 2 項演算子です。「<<」は指定された数だけビットを左へシフトします。シフトした結果、右側の空いた桁はビット 0 で埋まります。

 以下は、4 つ左シフトする例です。

[例]変数 a の値を 4 ビット分だけ左シフトする。

  
1: uint a = 0x0000001f, c;
2: c = a << 4; // c は、0x000001f0

 右シフトの場合は、「符号付き整数」( int など)か「符合なし整数」( uint など)かによって、ビットのシフトされ方が異なります。符号付き整数では、最上位のビットが符号を表し(ビットが 1 だとマイナス)、右にシフトするとき符号以外の部分が右にシフトし、符号の右隣に空いた桁は符号と同じビットで埋まります。その結果、1 ビット右にずれるごとに、値は 2 分の 1 になります。これを「算術右シフト」といいます。一方、符号なし整数では、符号ビットはなく、単純にすべてのビットが右にシフトします。これを「論理右シフト」といいます(符号付き整数や符号なし整数のビット表現の規則、その意味については、情報処理の用語や基礎知識を扱った書籍を参照してください)。

 下記の例では、1 行目の符号付き整数である変数 a の値は、0xfffffffc と最上位のビットが 1 になっています。2 行目での右へシフトした結果も、0xfffffffe というように、最上位のビットは 1 のままです。最上位の次のビットは、符号と同じビット 1 になっています(もし、符号以外の部分が左シフトしたとき、空いた桁がビット 0 になるのなら、値は 0xbffffffe になってしまいますが実際はそうはなっていません)。

  3 行目の符号なし整数である変数 u の値は 0xfffffffc と最上位のビットが 1 になっていますが、4 行目のシフトでは論理右シフトなので、最上位のビットもそのまま移動し、空いた桁には 0 が入ります(そのため 16 進表記の最上桁が f でなく、7 になっています)。計算結果は、0x7ffffffe になります。

[例]算術右シフトと論理右シフト

  
1: int a = -4, c; // a は 0xfffffffc
2: c = a >> 1;  // c は 0xfffffffe (最上位ビットはそのまま)
3: uint u = 0xfffffffc, v; 
4: v = u >> 1;  // v は 0x7ffffffe

 以下に、ビット演算のサンプルプログラムを提示します。String.Format メソッドを使って、16 進数表記文字列へ変換する書式文字列も取り入れておきました。実験プログラムをつくる際の参考にしてください。

[例]さまざまなビット演算

  
 1: namespace MySpace 
 2: {
 3: public class MyApp
 4: {
 5:  public static void Main(string [] args)
 6:  {
 7:   string s;
 8:   uint x, y;
 9:   // 論理和 (OR)
10:   x = 0x00ffff00; 
11:   y = x | 0xffffffff; 
12:   s = System.String.Format("{0:x8}",y); //8 桁 16 進数
13:   System.Console.WriteLine(s); 
14:   // 演算論理積 (AND)
15:   y = x & 0xffffffff;
16:   s = System.String.Format("{0:x8}",y);
17:   System.Console.WriteLine(s);
18:   // 排他的論理和 (XOR)
19:   y = x ^ 0xffffffff; 
20:   s = System.String.Format("{0:x8}", y);
21:   System.Console.WriteLine(s); 
22:   // 反転
23:   uint d = 0x00ff00ff, e; 
24:   e = ~d;
25:   s =System.String.Format("d={0:x8},e={1:x8}",d,e); 
26:   System.Console.WriteLine(s); 
27:   // 右シフト
28:   int a = -4, c; //a は 0xfffffffe 
29:   uint u = 0xfffffffc, v; 
30:   c = a >>1; // 算術右シフト
31:   s =System.String.Format("a={0:x8},r={1:x8}",a,c); 
32:   System.Console.WriteLine(s); 
33:   v = u >>1; // 論理右シフト 
34:   s =System.String.Format("u={0:x8},v={1:x8}",u,v); 
35:   System.Console.WriteLine(s); 
36:  } 
37: }
38:}

(実行結果)

  
    ffffffff
00ffff00
ff0000ff
d=00ff00ff,e=ff00ff00
a=fffffffc,r=fffffffe
u=fffffffc,v=7ffffffe
  

2-6-4  条件式に関するもの

 この後、**第 3 章「さまざまな制御構造」**で扱う条件判断の if 文では、条件式が登場します。条件式は、演算結果が bool 型となる計算式として扱うことができます。

[例] if 文(第 3 章「さまざまな制御構造」で説明)

  
1: if(x == y) { z = 1; }
2: else     { z = 0; }

[例]条件式は bool 型の計算式

  
1: bool b;
2: int x=3, y=4, z; 
3: b = (x == y); //b の値は、false;

 また、条件結果によって値が変わる演算子(条件オペレータ)もあります。条件式の値が true か false かに応じて、条件式の後ろの「?」に続く、2 つの値のうち 1 つが式の値として使われます。2 つの値は、コロン( : )で区切られます。もちろん値の項目は、単純な定数でなく計算式でも構いません。

~ = 条件式 ? 真のときの値 : 偽のときの値

[例]条件式 x == y の結果によって値が決まる

  
1: int x=3, y=4, z;
2: z = (x == y) ? 10 : 20; // 偽なので z の値は 20

 なお、上記の例にもあるように、「=」は値の代入で使い、「==」は値の比較で使います。この 2 つは厳密に使い分けられているので注意が必要です。

  2 つ以上の条件式を「または」、「かつ」でつないで複合条件を表すには、それぞれ「||」や「&&;vを使います。この演算子を使った条件式の評価結果も bool 型のデータになります。

[例]複合条件

  
1: int a=1, b=2, c=3, d=4; 
2: int z; 
3: z = (a==b && c<d) ? 10 : 20; //a==b かつ c<d、z は 20
4: z = (a==b || c<d) ? 10 : 20; //a==b または c<d、z は 10

 以下に、まとめのサンプルを表示します。

  
 1:namespace MySpace
 2:{ 
 3: public class MyApp 
 4: { 
 5:  public static void Main(string [] args) 
 6:  { 
 7:   bool b; 
 8:   int x=3, y=4, z;
 9:   // 等号
10:   b = (x == y); //b は false
11:   System.Console.WriteLine("x==y " + b); 
12:   // 条件オペレータ ~ ? 値 1 : 値 2 
13:   z = (x != y) ? 10 : 20;
14:   System.Console.WriteLine("z = " + z); 
15:   // 複合条件
16:   z = ((x == 4) || (y == 4)) ? 10 : 20;
17:   System.Console.WriteLine("z = " + z);
18:   z = ((x == 4) && (y == 4)) ? 10 : 20;
19:   System.Console.WriteLine("z = " + z);
20:  }
21: }
22:}

(実行結果)

  
    x==y False
z = 10
z = 10
z = 20
  

2-6-5  代入をともなう演算

  C# では、C/C++ と同様の代入をともなう演算子があります。

 以下の 2 つの計算式は同じ意味です。

[例]変数 a に 10 を加算する

  
1: int a = 0; 
2: a = a + 10; //10 加算
3: a += 10;  //10 加算

 つまり、代入をおこなう演算子は、2 項演算子の計算に使われた変数自身に対して、その計算結果を代入する場合に使います。この代入をともなう演算は、四則演算や余りの計算のほか、論理和や論理積などのビット演算、またビットシフトなどでも利用することができます。

[例]代入をともなうさまざまな演算

  
 1:namespace MySpace
 2:{
 3: public class MyApp
 4: { 
 5:  public static void Main(string [] args)
 6:  {
 7:   int a = 0;
 8:   a += 10; //10 加算
 9:   a -= 5; // 5 減算
10:   a <<= 2; // 左へ 2 桁ビットシフト( 4 倍になる)
11:   System.Console.WriteLine("a="+a); //a=20 
12:  }
13: }
14:}