第9章 演算子

 

9.1 演算子の種類

省略

 9.2 算術演算子

省略

9.3 関係演算子

a = b > c; // bがcより大ならaは1、そうでなければaは0

9.4 等価演算子

省略

 9.5 論理演算子

省略

 9.6 インクリメント/デクリメント演算子

省略

 9.7 ビット演算子

/* &の処理:ビットごとのAND */

処理方法:同じ位置のビットを見て、どちらも1なら結果を1にする

記述例:a = 0x5555 & 0x00FF; // aは0055になる

    0101 0101 0101 0101 (5555)

and)0000 0000 1111 1111 (00FF)

a = 0000 0000 0101 0101 (0055)

/* |の処理:ビットごとのOR */

処理方法:同じ位置のビットを見て、片方または両方に1があれば結果を1にする

記述例:a = 0x5555 & 0x00FF; // aは55FFになる

    0101 0101 0101 0101 (5555)

or )0000 0000 1111 1111 (00FF)

a = 0000 0000 0101 0101 (0055)

/* ^の処理:ビットごとのXOR */

処理方法:同じ位置のビットを見て、両者が異なっていれば結果を1にする

記述例:a = 0x5555 ^ 0x00FF; // aは55AAになる

    0101 0101 0101 0101 (5555)

xor)0000 0000 1111 1111 (00FF)

a = 0101 0101 1010 1010 (55AA)

/* ~の処理:ビットの反転(1の補数) */

処理方法:ビットが1なら0に、0なら1に反転する

記述例:a = 0x05AF;

a = ~a; // aはFA50になる

補数)0000 0101 1010 1111 (05AF)

a =  1111 1010 0101 0000 (FA50)

 9.8 シフト演算子

/* 左シフト */

int a;

a = 0xF000000F;

a = a << 2; // aはC000003C

aのビット並び(シフト前):1111 0000 0000 0000 0000 0000 0000 1111

aのビット並び(シフト後):1100 0000 0000 0000 0000 0000 0011 1100

/* 符号なし右シフト */

a = 0xF000000F;

a = a >> 2; // aは3C000003

aのビット並び(シフト前):1111 0000 0000 0000 0000 0000 0000 1111

aのビット並び(シフト後):0011 1100 0000 0000 0000 0000 0000 0011

/* 符号あり右シフト

(1)符号あり整数の算術右シフト:(この処理系が多い) */

a = 0xF000000F;

a = a >> 2; // aはFC000003

(2)浮動あり整数の論理右シフト:(符号なし右シフトと同じ動作)

a = 0xF000000F;

a = a >> 2; // aは3C000003

 9.9 代入演算子

/* 単純代入演算子と複合代入演算子 */

longname[3][4] = longname[3][4] + 10; // 通常の記述

longname[3][4] += 10; // 複合代入形式で記述

/* 多重代入式 */

a = 10; b = 10; c = 10;

a = b = c = 10;

と書くことができます。

これは

a = (b = (c = 10));

と解釈され、すべての変数に10を代入します。

/* 代入演算子の結合順序 */

省略

 9.10 条件演算子

/* 条件演算子の記述 */

式1 ? 式2 : 式3

条件式である式1を評価し、

それが真なら式2を、

そうでなければ式3を結果とします。

例1

ss = a > 0 ? "yes" : "no";

 真ならssは"yes"。

例2

a > 0 ? cout << "yesn" : cout << "non";

 真なら"yes"を表示する。

例3

cout << (a > 0 ? "yesn": "non");

 真なら"yes"を表示する。()が必要なので注意。

例4

return a > b ? a : b;

 大きいほうの値を返す。

例5

x = a > 0 ? b = 10 : b = 20;

 結果により変数bに10または20を代入。同じ値を変数xにも代入。

例6

for (int n = 11; n<=40; n++) {

cout << n << (n % 10 ? ' ' : 'n');

}

以下のように10ごとに改行して表示。

11 12 13 14 15 16 17 18 19 20

21 22 23 24 25 26 27 28 29 30

31 32 33 34 35 36 37 38 39 40

/* 多重の条件演算子 */

複数の条件演算子を続けて書くことができます。たとえば、

if (a > 1000)

b = 1000;

else if (a > 100)

b = 100;

else if (a > 10)

b = 10;

else

b = 0;

と似た記述を、条件演算子で書くと次のようになります。

()は不要ですが、明快さのため入れています。

b = (a > 1000) ? 1000 : (a > 100) ? 100 : (a > 10) ? 10 : 0;

 9.11 カンマ演算子

a = 10;

c = 20;

という文にカンマ演算子を用いると、

a = 10, c = 20;

となります。

例1:ブロックを使わずに「ひとつの文」を表現する

普通の記述:{}を使ってふたつの文を制御している

if (a > 0) {

b = 10;

c = 20;

}

カンマ演算子を使った記述:ひとつの文を制御する

if (a > 0) b = 10, c = 20;

例2:ひとつのタイミングでふたつの動作を行なう

普通の記述:数値入力とその判定を別の行で行なう

cin >> dt; // 最初の入力

while (dt != 999) { // 999でなければ処理続行

cin >> dt; // 2回目以降の入力

}

カンマ演算子を使った記述

while (cin >> dt, dt != 999) { // この行だけで記述できる

}

例3:for文とともに使う

普通の記述:使用する変数の初期設定をループ外で行なっている

sum = 0; // ループ外で処理設定

mul = 1; // ループ外で処理設定

for (i = 1; i <= 5; i++) {

sum += i;

mul *= i;

}

カンマ演算子を使った記述1:初期設定をfor文の第1項で行なう

for (sum = 0, mul = 1,i = 1, i <= 5; i++) {

sum += i;

mul *= i;

}

カンマ演算子を使った記述2:加算と乗算もfor文の第3項で行なう

for (sum = 0, mul = 1, i = 1; i <= 5; sum += i, mul *= i, i++) {

}

 9.12 sizeof演算子

sizeof演算子は引数のサイズをバイト数で報告します。

書式は次のとおりです。

sizeof 単項式;

sizeof (型識別子);

次に例を示します。

int型は4バイトであるとします。

9_9-12

/* 式を評価しない */

int a, b = 100;

a = sizeof(++b); // aは4。この++bは評価されない

/* charのサイズ報告 */

n = sizeof 'a'; // C++では1(char型)、Cでは4(int型)

/* sizeof演算子の目的 */

プログラムからマシン依存部を除くために用います。

たとえばint型変数1000個分のサイズを必要としたとき、プログラムの中で、

n = 4 * 1000; // int型が4バイトとは限らないので

n = sizeof(int) * 1000; // こうしたほうが安全

とするのが安全です。

また、構造体のサイズを得るときも、

必要バイト数を数えるより、処理系まかせにして、

n = sizeof(Stdata);

としたほうが簡単です。

またsizeof演算子を使うと、配列宣言の配列長を知ることもできます。

int idt[8];

n = sizeof idt/sizeof idt[0]; // nは配列長の8になる

n = sizeof idt/sizeof(int); // これでもよい

/* size_t型 */

sizeof演算子の返す値は「size_t型」です。

これは<cstddef>というヘッダの中で、たとえば、

typedef unsigned int size_t;

のように宣言されています。

通常、プログラマはこの戻り値の型を気にすることはありません。

int型の変数に戻り値を代入すれば十分です。

しかし大きなオブジェクトを扱うときは、指定どおり、

size_t sz;

sz = sizeof(someobject);

のようにしたほうが好ましいといえます。

 9.13 ポインタ演算子

*と&に関して

省略

 9.14 キャスト演算子

(型)式 // C形式キャスト演算

単純型名(式) // 関数形式キャスト演算

static_cast<目的の型> (式) // 静的キャスト

dynamic_cast<目的の型> (式) // 動的キャスト

const_cast<目的の型> (式) // 定値性キャスト

reinterpret_cast<目的の型> (式) // 強制キャスト

使い方については28章で記述

 9.15 メンバポインタ演算子

. // クラス/構造体/共用体が実体表現されたときのメンバ参照に用いる

-> // クラス/構造体/共用体がポインタとして表現されたときのメンバ参照に用いる

9_9-15

 9.16 new演算子とdelete演算子

/* new演算子の基本用法 */

new演算子は実行時に、動的記憶域(動的メモリ、あるいはヒープメモリとも呼ばれる)に

オブジェクトを確保し、確保領域の先頭アドレスを返します。

new演算子で確保されたメモリ領域はdelete演算子で解放できます。

解放したメモリ領域は、再利用可能になります。

例1:単純データの確保

int *p;

p = new int; // int型オブジェクトを確保

delete p; // それを解放

例2:配列データの確保

int *p;

p = new int[20]; // int型配列を確保

delete p; //配列を解放。[]で配列解放指示

例3:クラスを確保

Mycls *p; // Myclsは自作のクラスとする

p = new Mycls; // Mycls型オブジェクトを確保

delete p; // それを解放

例4:宣言時に同時に確保

int *p = new int[20]; // int型配列を確保

/* new演算子と初期化 */

new演算子利用時に初期化を行なうことができます。

ただし単一のオブジェクトの場合だけ可能です。

new演算子では配列の場合の初期化はできないので注意してください。

int *p;

p = new int(123); // *pの初期値は123になる

p = new int(); // *pの初期値は0になる

Mycls *mp;

mp = new Mycls(); // Myclsのコンストラクタで初期化

mp = new Mycls(10, 20); // Myclsのコンストラクタで初期化

/* new演算子による多次元配列確保 */

たとえば2次元配列を確保するときは、次のようにします。

この例はint型の[3][4]という配列を用意し、その先頭アドレスをpに設定します。

int (*p)[4];

p = new int[3][4]; // 多次元配列を確保

*(*(p+1)+2) = 1234; // 要素指定例

p[1][2] = 1234; // これでもよい

delete[] p; // 配列全体を解放

ここで宣言時のポインタ名は(*p)のようにかっこ付きにします。

かっこがないと、

int *p[4];

となり、これはint型ポインタが単に4個集まった配列になります。

/* 配列添字の式指定 */

配列確保するときの添字は「式」で指定できます。

ただし多次元配列の場合の、2次元目以降の添字は定数で行なう必要があります。

int n = 4, *p, (*pp)[4];

p = new int[n]; // OK

pp = new int[n][4]; // OK

pp = new int[4][n]; // エラー

/* delete演算子の有効性 */

new演算子でクラスオブジェクトを確保した場合、コンストラクタが実行されますが、

逆にこのオブジェクトをdelete演算子で確保した場合は、

適切にデストラクタが呼び出されます。

Mycls *mp;

mp = new Mycls(); // コンストラクタが呼ばれる

delete mp; // デストラクタが(あれば)呼ばれる

/* new演算子のエラー処理 */

小さな動的メモリサイズを確保する場合、new演算子は単純な利用でかまいません。

しかし動的メモリを大量に利用するときは、エラー対策が必要です。

動的メモリ領域は無限にあるわけではありません。

動的メモリ領域を使い果たすと、

次に実行するnew演算子はエラー(メモリ確保失敗)になります。

ユーザーは、必要に応じて、メモリ確保失敗時の処置も適切に記述する必要があります。

new演算子のエラー報告手法は、次のようになっています。

(1)new演算子はメモリ確保に失敗するとデフォルトでは例外処理を行なう

(2)nothrow指定があると、失敗時にヌル(0)を返す

次にエラー処理の記述例を示します。

なおnewに関する機能はヘッダ<new>の中で定義されています。

例外機能によるエラー処理の例

char *p;

try {

p = new char[0x7fffffff]; // メモリ確保できなければ

}

catch (bad_alloc e) {

cout << "ERR!n"; // エラー処理

}

nothrowオプションを指定したエラー処理の例

p = new(nothrow) char(0x7fffffff]; // メモリ確保できなければ

if (!p) {

cout << "ERR!n"; // エラー処理

}

 9.17 演算子の優先順位と結合規則

/* 優先順位 */

意図的に優先順位を指定したいときは、その部分を()で囲みます。

省略

/* 結合規則 */

省略