B.演算子

 Copyright(C) 13Jan2003 coskx

演算子についての説明である。内容は以下のとおりである。
1.C言語における算術演算子
2.C言語におけるビット演算子
3.C言語における条件の表現
4.sizeof演算子
APPENDIX 1 BOOL演算
APPENDIX 2 演算子の優先順位

B.1 C言語における算術演算子

算術演算子
演算子

意味

=

左辺の値を右辺の変数に代入する
例 x = a+b;  「a+b=x;」とは書けない
(右辺と左辺が等しいと言う意味ではない)

+

加算

-

減算

*

乗算「×」

/

除算「÷」

%

剰余(割ったあまりを求める)
整数演算のみ有効
例 a%b aをbで割ったあまりを求める演算

++

「a++」「++a」は「a=a+1」と同じ
「c=a++;」は「c=a; をして次に a++; をする」の意味
「c=++a;」は「++a; をして次に c=a; をする」の意味

--

「a--」「--a」は「a=a-1」と同じ
「c=a--;」は「c=a; をして次に a--; をする」の意味
「c=--a;」は「--a; をして次に c=a; をする」の意味

+=

「a+=b;」は「a=a+b;」の意味

-=

「a-=b;」は「a=a-b;」の意味

*=

「a*=b;」は「a=a*b;」の意味

/=

「a/=b;」は「a=a/b;」の意味

%=

「a%=b;」は「a=a%b;」の意味

加減乗除の優先順位

加減乗除の優先順位は算数の常識と同じであり,乗除が優先し加減はその後である。
乗除算同士の場合,あるいは加減同士の場合は左から順に行なわれる。
ここでは定数で説明しているが,値の代入された変数の場合でも同様である。

例 100/2*3  →(100/2)*3 →50*3 →150
  100/(2*3)  →100/6 →16
  100/2/5  →(100/2)/5 →50/5 →10
  10*2+2*4 →(10*2)+(2*4) →20+8 →28
  20*(2+2)*4 →20*4*4 →80*4 →320

整数(小数点のない数,intなど)と実数(小数点以下がある数,doubleなど)の混合演算

演算は優先順位にもとづいて順番に実行されるが,
整数と実数の混合演算の時には,混合演算が起こる直前で整数が実数化されてから演算される。
値の代入された変数の場合でも同様である。


  100/3  →33 (これは整数演算なので小数点以下は消える)
  100/3.0  →100.0/3.0 →33.33333
  100/(2.0*3)  →100/(2.0*3.0)  →100/6.0  →100.0/6.0 →16.66667
  100.0*(1/2)  →100.0*0 →100.0*0.0 →0.0 (1/2は整数演算なので小数点以下は切り捨てられ0になる)
  100*(1.0/2)  →100*(1.0/2.0) →100*0.5 →100.0*0.5 →50.0
  (4/3)*10.0 →1*10.0 →1.0*10.0 →10.0
  (4.0/3)*10 →(4.0/3.0)*10 →1.333333*10 →1.333333*10.0 →13.33333

変数を用いた例
  int a=11,b=12,c;
  double d;
    とする
  c=(a+b)/2 →(11+12)/2 →23/2 →c=11 cには11が入る
  d=(a+b)/2.0 →(11+12)/2.0 →23/2.0 →23.0/2.0 →d=11.5 dには11.5が入る
  c=(a+b)/2.0 →(11+12)/2.0 →23/2.0 →23.0/2.0 →c=11.5 cには11が入る(cはint型なので)
  d=(a+b)/2 →(11+12)/2 →23/2 →d=11 dには11.0が入る

  cast演算(型変換演算)の例
  (double)aの表現は,int型変数aを一時的にdouble型変数とみなせという意味になる

  d=((double)a+(double)b)/2.0 →((double)11+(double)12)/2.0
    →(11.0+12.0)/2.0 →23.0/2.0 →d=11.5 dには11.5が入る
  d=((double)a+b)/2 →((double)11+12)/2 →(11.0+12)/2
    →(11.0+12.0)/2 →23.0/2 →23.0/2.0 →d=11.5 dには11.5が入る

++,--について

「++」「--」は,変数の前に置かれた場合は前置演算子,変数の後に置かれた場合は後置演算子と呼ばれる。

例 後置演算子
 y=x++;   printf("n=%d\n",n++);   *p++=*q++;   i++;
 y=x--;   printf("n=%d\n",n--);   *p--=*q--;   i--;
例 前置演算子
 y=++x;   printf("n=%d\n",++n);   *(++p)=*(++q);   ++i;
 y=--x;   printf("n=%d\n",--n);   *(--p)=*(--q);   --i;

後置演算の意味
 式を評価して作業が終わったら,後置演算を行う。

意味
y=x++;  y=x を実行してから,x=x+1を実行
printf("n=%d\n",n++); printf("n=%d\n",n) を実行してから,n=n+1
*p++=*q++; *p=*qを実行してから,p=p+1,q=q+1
(演算子の優先順位より,*(p++)=*(q++);の意味になる)
i++;

これは単にi=i+1

前置演算の意味
 先に前置演算を行い,次に式を評価して作業を行う。

意味
y=++x;  x=x+1 を実行してから,y=xを実行
printf("n=%d\n",++n); n=n+1 を実行してから,printf("n=%d\n",n)
*(++p)=*(++q); p=p+1,q=q+1を実行してから,*p=*q
++i;

これは単にi=i+1 (i++;と動作は同じ)



プログラムの一部 実行結果
int x=100,y=100;
printf("x=%d y=%d\n",++x,y++);
printf("x=%d y=%d\n",x,y);
x=101 y=100
x=101 y=101

等号についての補足(中級者向け説明)
「a=b;」は「bの値をaに代入する」という意味であった。
ところでC言語では式「a=b」そのものが値を持ち,「c= (a=b)」が意味を持つ。『「a=b」そのものの値』とは「左辺に代入された値」のことである。すなわち,「c=(a=b)」では,まず始めにbの値が aに代入され,次に『式「a=b」の値』(それはaの値)がcに代入されるということになる。次のプログラムの実行状況を追跡するとこのことが確認でき る。
List 等号についての補足

#include <stdio.h>

int main()
{
    int a;
    double x=2.5;
    double y;
    y=a=x;
    printf("x=%.2f a=%d y=%.02f\n",x,a,y);
    y=(a=x);
    printf("x=%.2f a=%d y=%.02f\n",x,a,y);
    printf("(a=x) = %d\n",(a=x));
    return 0;
}

/*****実行結果*****
x=2.50 a=2 y=2.00
x=2.50 a=2 y=2.00
(a=x) = 2
******************/



B.2 C言語におけるビット演算子

ビット演算は整数型変数(char型,int型など)に対して行なわれる演算である。変数のビットパターンに対する演算である。

(1)シフト演算

演算子

意味

<<

左シフト

>>

右シフト

この演算は,整数におけるコンピュータの内部表現において,右や左にずらす演算である。(もともとコンピュータ内部で数値は,2進数で扱われているビットパターンである)
例えば
「12<<3」は12を左に3つずらす意味であり,12はビットパターンで表すと「1100」なので左に3つずらすと「1100000」となりこれは96である。
「12>>2」は12を右に2つずらす意味であり,12はビットパターンで表すと「1100」なので右に2つずらすと「11」となりこれは3である。
一般に整数を左に1つずらすと2倍になり,2つずらすと4倍になり,3つずらすと8倍になり4つずらすと16倍になる。逆に右に1つずらすと1/2倍になり,2つずらすと1/4倍になり,3つずらすと1/8倍になり,4つずらすと1/16倍になる。

int x,y;
の時
y=x/1024;

y=x>>10;
は同じ意味になる。

(2)論理演算

演算子

意味

&

2つの値のビットごとのand演算

|

2つの値のビットごとのor演算

^

2つの値のビットごとのxor演算(排他的OR)

~

ある値の全ビット反転


 「157&241」はビットパターンでは「10011101&11110001」となり,以下のように結果は10010001すなわち145となる

  1001 1101
& 1111 0001 ビットごとに縦にアンド演算を行う。繰り上がり繰り下がりはない。
-------------
  1001 0001

もし
int x=157;
int y=241;
int z;
なら
z=x&y;
の演算で,zには145が代入される。

 

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

第2変数

1111 0001

F1

241

結果

1001 0001

91

145

例 「157|241」はビットパターンでは「10011101|11110001」となり,以下のように結果は11111101すなわち253となる

  1001 1101
| 1111 0001 ビットごとに縦にオア演算を行う。繰り上がり繰り下がりはない。
-------------
  1111 1101

もし
int x=157;
int y=241;
int z;
なら
z=x|y;
の演算で,zには253が代入される。

 

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

第2変数

1111 0001

F1

241

結果

1111 1101

FD

253

 「157^241」はビットパターンでは「10011101^11110001」となり,以下のように結果は01101100すなわち108となる

  1001 1101
^ 1111 0001 ビットごとに縦に排他オア演算を行う。繰り上がり繰り下がりはない。
-------------
  0110 1100

もし
int x=157;
int y=241;
int z;
なら
z=x
^y;
の演算で,zには108が代入される。

 

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

第2変数

1111 0001

F1

241

結果

0110 1100

6C

108

例 「~157」はビットパターンでは「~10011101」となり,以下のように結果は01100010すなわち98となる

~ 1001 1101  ビットごとに反転演算を行う。繰り上がり繰り下がりはない。
-------------
  0110 0010

もし
unsigned char x=157;
unsigned char z;
なら
z=~x;
の演算で,zには98が代入される。

 

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

0110 0010

0

62

98

使用例

int k;
のもとで
if (k%256==0) {

if ((k&0xff)==0) {
と同じ意味になる。2のべき乗の時のみ使える

int k,k1,k2,k3;
のもとで

k1=k2=k3=0;
for (k=0; k<10000; k++) {
    k1++;
    k1&=3;
    k2++;
    k2&=7;
    k3++;
    k3&=0xf;
    :
    :
}

では
k1の値は0,1,2,0,1,2,0,1,2,・・・
のように0,1,2を繰り返し,
k2の値は0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,・・・
のように0,1,2,・・,7を
繰り返し,
k3の値は0,1,2,3,・・,13,14,15,0,1,2,3,・・,13,14,15,0,1,2,3,・・,13,14,15,0,1,2,3,・・
のように0,1,2,・・,13,14,15を繰り返す。

unsigned int x:
のもとで,

if (x&1==1)  はxの第0ビット(右端)が1だったらの意味になる.
if (x&2==2)  はxの第1ビット(右から2番目)が1だったらの意味になる.
if (x&4==4)  はxの第2ビット(右から3番目)が1だったらの意味になる.
if (x&8==8)  はxの第3ビット(右から4番目)が1だったらの意味になる.
if ((x&0x10)==0x10)  はxの第4ビット(右から5番目)が1だったらの意味になる.

(3)ビット演算子利用上の注意

ビット演算子の優先順位は低いので「()かっこ」を利用しておくとよい。
例 int k1,k2 のもとで
k2=k1<<4+1;
はそのままでは
k2=k1<<(4+1);
のように解釈される。もし
k2=(k1<<4)+1;
の記述をしたければ,そのように書く必要がある。同様に
if (k2==k1&0xff) {
はそのままでは
if ((k2==k1)&0xff) {
のように解釈される。もし
if (k2==(k1&0xff)) {
の記述をしたければ,そのように書く必要がある。

(4)「ビット演算子」+「=」

ビット演算子においても「&=」などの表現ができる。

意味

x <<= 3;

x = x<<3;

x >>= 4;

x = x>>4;

x &= 0xf;

x = x&0xf;

x |= 0x55;

x = x|0x55;

x ^= 0xffff;

x = x^0xffff;


(5)unsigned. signed変数と「ビットシフト演算子( <<,>> )」
左シフト「<<」は1つシフトする毎に2倍される。signed変数では負の値である場合もあるが,
その場合でも1つシフトする毎に2倍される関係は保たれている。
unsigned変数では,変数の最上位ビットを越えるとオーバーフローして正しくない値になる。
signed変数では最上位ビットが符号ビットとなっているため,最上位ビットが変化する場合には,
オーバーフローして正しくない値になる。

signed short int (16bit)の場合の例
演算
x
y
y= x<<2
5 (101)
-5 (1111 1111 1111 1011)
28672 (0111 0000 0000 0000)
20 (1 0100)
-20 (1111 1111 1110 1100)
-16384 (1100 0000 0000 0000) OF

unsigned short int (16bit)の場合の例
演算
x
y
y= x<<2
5 (101)
14336 (0011 1000 0000 0000)
28672 (0111 0000 0000 0000)
20 (1 0100)
57344 (1110 0000 0000 0000)
49152 (1100 0000 0000 0000) OF


右シフト「>>」は1つシフトする毎に1/2倍される。signed変数では負の値である場合もあるが,
その場合でも1つシフトする毎に1/2倍される関係は保たれている。(最上位ビットが残される)
-1を1つシフトしても値は変わらない。

signed short int (16bit)の場合の例
演算
x
y
y= x>>2
20 (1 0100)
-20 (1111 1111 1110 1100)
-8192 (1110 0000 0000 0000)
-1 (1111 1111 1111 1111)
5 (101)
-5 (1111 1111 1111 1011)
-2048 (1111 1000 0000 0000)
-1 (1111 1111 1111 1111)

unsigned short int (16bit)の場合の例
演算
x
y
y= x>>2
20 (1 0100)
57344 (1110 0000 0000 0000)
5 (101)
14336 (0011 1000 0000 0000)



B.3 C言語における条件の表現

条件式一覧
数学で「ab」と表現される関係をC言語では「a<=b」と書く。同様な表現を以下の表に示す。
次のような使われ方をする。
while (a<=b)
if (a<=b)
while((b<a)&&(a<c))
数学の表現 C言語の表現 意味
a=b a==b aとbが等しい
a<b a<b aはbより小さい
a>b a>b aはbより大きい
a≦b a<=b (a=<b は誤り) aはb以下
a≧b a>=b (a=>b は誤り) aはb以上
a≠b a!=b (a=!b は誤り) aはbと等しくない
(a<b)or(c<a) (a<b)||(c<a) aはbより小さいか,またはaはcより大きい
(b<a)and(a<c) (b<a)&&(a<c) aはbより大きく,かつaはcより小さい
(a=b)and(c=d)and(e=f) (a==b)&&(c==d)&&(e==f) aとbが等しく,かつcとdが等しく,かつeとfが等しい
(b<a)and(a≠c) (b<a)&&(a!=c) または
(b<a)&&(!(a==c))
aはbより大きく,かつaはcと等しくない

ドモルガンの定理
ドモルガンの定理により同値な関係
!((a<b)&&(c<d)) (!(a<b))||(!(c<d))
!((a<b)||(c<d)) (!(a<b))&&(!(c<d))

よくある条件式の誤り(その1)
   表現
数学の表現 x<3,7<xの場合は.....
誤ったC言語による表現 if (x<3,7<x) {
文法上は誤りではないのでコンパイルエラーにはならない
if (7<x) { 
と同じ意味になってしまう

考え方

数学の表現をより論理的に書き直すと
「x<3または7<xの場合は.....」
となる
正しいC言語による表現 if ( x<3 || 7<x ) {

よくある条件式の誤り(その2)
   表現
数学の表現 3≦x≦7の場合は.....
誤ったC言語による表現 if (3<=x<=7) {
文法上は誤りではないのでコンパイルエラーにはならない
if ((3<=x)<=7) { 
の意味になってしまい,次の中級者向け説明に示す解釈になり,
このif文は常に真になってしまう。

考え方

数学の表現をより論理的に書き直すと
「3≦xかつx≦7の場合は.....」
正しいC言語による表現 if ( 3<=x && x<=7 ) {

条件の真偽のC言語での取り扱い(中級者向け説明)

C言語では真偽を数値で表すことになっていて,偽は0,真は0以外の値ということになっている。
例えば次のテストプログラムで
(1)「i<3」は値を持ち,偽では0,真では1になっている。
(2)「if (i) 」はiが0の時,偽とみなされ,iが0以外では真とみなされている。

List 条件式の値の検証プログラム

include <stdio.h>

int main()
{
    int i;
    int tf;
    for (i=0;i<5;i++) {
        tf=(i<3);
        printf("i=%d tf=%d ",i,tf);
        if (i<3) {
            printf("i<3\n");
        } else {
            printf("3<=i\n");
        }
    }
    for (i=-5;i<5;i++) {
        printf("i=%d ",i);
        if (i) {
            printf("true\n");
        } else {
            printf("false\n");
        }
    }
    return 0;
}

/****実行結果****
i=0 tf=1 i<3
i=1 tf=1 i<3
i=2 tf=1 i<3
i=3 tf=0 3<=i
i=4 tf=0 3<=i
i=-5 true
i=-4 true
i=-3 true
i=-2 true
i=-1 true
i=0 false
i=1 true
i=2 true
i=3 true
i=4 true
****************/

例えば,同様に,引数で与えた整数値が文字コードで大文字であるかどうか調べる関数int isupper(int ch)は,
偽なら0,真なら1を返すので
if (isupper(ch)==1) {......

if (isupper(ch)) {.........
は同じ意味になるため,通常は後者で記述される。

また永久ループは
while(1) {............
のように記述される。


B.4 sizeof算子

sizeof演算子は,変数の型,変数,配列変数が何バイトで出来ているかを計算する演算子である。

例えば
sizeof(char)は1,sizeof(short int)は2,sizeof(long int)は4を表す。

また
char ch;
short int sx;
の時
sizeof(ch)は1,sizeof(sx)は2を表す。

char mystring1[]="abcdefg";
char mystring2[100]="ABCDEFG";
の時
sizeof(mystring1)は8を表す。(文字列終端も含まれる)
sizeof(mystring2)は100を表す。

short int xx[]={123,456,789,234,567,890,987,654,321};
int num;
の時
num=sizeof(xx)/sizeof(short int);
で,numには要素数9が入る。

変わった例では定数の内部表現を見ることが出来る
printf("%d %d %d %d\n",sizeof(123),sizeof(123.123),sizeof('A'),sizeof(1==1));
では,windowsでは「4 8 4 4」が得られた。整数扱いの数はintになり実数扱いの数はdoubleになるようだ

 APPENDIX 1 BOOL演算

C言語で用いられるブール演算は基本的に4つあり,演算対象とする値は0と1である。
(1)アンド演算(and)

演算

結果

0 and 0

0

0 and 1

0

1 and 0

0

1 and 1

1

「両方1の組み合わせのみ1で,その他の組み合わせは0」

演算

結果

x and 0

0

x and 1

x


(2)オア演算(or)

演算

結果

0 or 0

0

0 or 1

1

1 or 0

1

1 or 1

1

「両方0の組み合わせのみ0で,その他の組み合わせは1」

演算

結果

x or 0

x

x or 1

1


(3)ノット演算(not)

演算

結果

not 0

1

not 1

0

(4)イクスクルーシブオア演算(xor)(排他的論理和)

演算

結果

0 xor 0

0

0 xor 1

1

1 xor 0

1

1 xor 1

0

「お互いに値が異なる組み合わせで1,お互いに同じ値の組み合わせは0」

演算

結果

x xor 0

x

x xor 1

not x

 APPENDIX 2 演算子の優先順位

C言語の演算子には実行優先順位がある。算術演算において乗算除算が加算減算に優先することも,C言語の演算子の実行優先順位に含まれている。

順位 演算子 説明

1

[ ]  ( )  . -> 後置 ++ 後置 -- [ ] は配列,「. ->」は構造体メンバの区切り

2

前置 ++ と前置 --

3

sizeof  &   *   +  -  ~  ! すべて単項演算子 sizeof  &(アドレスを表す)   *(ポインタ)   +(+3など)  -(-5など)  ~  !

4

キャスト 例 double x; の場合 (int)x

5

*  /  % 2項演算子 乗算、除算、剰余

6

+ - 2項演算子 加算、減算

7

<< >> ビットごとのシフト

8

< > <=  >= 関係演算子

9

==  != 関係演算子

10

& ビットごとの AND

11

^ ビットごとの排他的 OR

12

| ビットごとの OR

13

&& 条件論理の AND

14

|| 条件論理の OR

15

? : 3項演算子

16

=  *=  /=  %=
+=  -=  <<=  >>=
&=  ^=  |=
単純代入と複合代入

17

, 順次評価