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:}