パソコン活用研究シリコンバレー(C、C++、の活用研究)

Cの演算子

演算に関しては、C も BASICもそう大きな違いはないようです。
しかし、C独特の部分や微妙に違っていたりする部分もあります。さしあたって、BASICと
異なる部分を中心に取り上げてみます。
なお、このページでは取り上げなかったものとしてcast演算子や、データ型が混在した計算における
データ型と精度の問題等があります。これについてはcast演算子とオートキャストをご参照下さい。

1 演算子記号の違い
以下の演算子記号がBASICとCで違います

  多くのBASIC  C    
剰余 mod %
等しい = ==
等しくない <> !=
論理積 and &&
論理和 or ||
否定 not !


2 インクリメント、デクリメント演算子
Cでは、変数につける ++、--  という演算子があります。
n++ は n=n+1  n-- は n=n-1 と同じです。

この演算子には前置(++n)と、後置(n++)があります。
次のような式の場合は、前置と後置で結果が違います。
x=++n  加算されてから代入  すなわち x=n+1 
x=n++  代入されてから、加算  すなわち x=n で n=n+1


3 ビット演算子、シフト演算子
Cのビット演算子には、&(ビット毎and)、 |(ビット毎or)、 ^(ビット毎xor)があります。
論理演算子の論理積(&&)、論理和(||)と演算子が異なるので、注意! 
アセンブラのand,or,xorとほぼ同じです。
ちなみに、BASICの場合は論理演算の論理積、論理和とこのビット演算を、and, orで兼用してます。

(例)   0xは16進数を表す
r = 0x3f & 0x15 ---->0x2f : 00101111
                     0x15 : 00010101
              -----------
                            00000101 --> r = 0x5 

シフト演算子もアセンブラ(機械語)のシフト命令とほぼ同様です
<< 左シフト
>> 右シフト
右シフトの場合、最上位ビットをそのまま保存する算術シフト(符号ありデータの場合)と
最上位ビットに0を補充する論理シフト(符号無しデータの場合)にわかれます

(例)
a = 0x7
x = a<<2  −−>aを2ビット左シフト  0x7 : 00000111
                        00011100   -->0x1cになる

シフト演算子はBASICになかった演算子です。旧BASICでこれをやろうとすると、結構しんどいです。


4 代入演算子

Cのへんてこな演算子に代入演算子というのがあります。
代入演算子 普通の記述
x += y x =x+y
x -= y x =x-y
x *= y x =x*y
x /=y x =x/y
x <<=y x = x<<y

上の表は全部の演算子を網羅してませんが、他の演算子でも同様の記述ができます。
通常の数学での記述とかけ離れていて、慣れないとちょっと奇異です。

また、BASICと違い、
x=y=z=5
というような記述の仕方ができます。各演算の結合規則は後述しますが、代入の場合は、
右−>左 に演算が実行されます。この例の場合、まず z=5 が実行され、ついで y=z が
実行され最後に x=y が実行されます。


5 アドレス演算子と間接演算子
ポインタという話を画面に出力、キーボードから入力でやりました。
そこで、”&変数” と記述すると、その変数のアドレスを表すと説明しました。その & が
アドレス演算子です。

その逆に
int *x;
と記述すると、x がポインタ(アドレス)で、*x が変数の値を表すと説明しました。この * が
間接演算子です。 

ポインタについては、別の個所でもまた取り上げていきますので、ここではこれにてごめん
ということにします。

6 条件演算子
これもBASICにはないC独特の演算子。
式1? 式2 : 式3 
という書式を使い、式1が真なら式2の値をとり、偽なら式3の値をとります。
例えば、 a = (b ? (c = d) : (c = e)) ;というような式がCには登場します。BASICでは見かけない
ので、最初は??かもしれません。これは、
bが真(0以外)ならc =d
bが偽(0)ならc = e
そしてa に cの値を代入ということになります。
if文を使えば
if (b) c = d;
else c = e;
a = c;
ということになります。

7 sizeof
データ型や構造体で確保した記憶領域の大きさ(byte)を表します。
sizeof(double)
のように使います。この場合double型のデータサイズが返ります。
よくメモリ管理関数のmallocで確保する領域のサイズを取得する時に使用します。
*mallocについては、メモリの動的割り当てを参照して下さい。

8 関係演算子と論理演算子

大小比較のための関係演算子(比較演算子)

< より大きい
> より小さい
<= 等しいか、より大きい
>= 等しいか、より小さい
== 等しい
!= 等しくない

論理演算子

&& 論理積
|| 論理和
! 否定

C言語では、関係演算子、論理演算子の結果0を偽とし、0以外の値を真とみなします。
C言語には論理型(boolean型)がないので、これらの値はint型である点は注意が必要です。(※)

C++には論理型が導入されており、真偽の判定にはTrue,Falseが使われます。

以下のプログラムを実行してみると、比較演算の結果が偽の場合は0,
真の場合は1を返しています。

(bool.c)
#include <stdio.h>

int main(void)
{
int a=3,b=5;
printf("a>b: %d\n",a>b);
printf("a<b: %d\n",a<b);
printf("!(a<b): %d\n",!(a<b));
return 0;
}

c:\bcc55\Bin>bool
a>b: 0
a<b: 1
!(a<b): 0


(※)
C99以降、Cにもbool型が導入されて、true,falseが使えるようにはなっています。
最近のコンパイラでは、stdbool.hというヘッダファイルが用意されており、そこに
以下のように定義されています。(Visual Studio2022のVCのstdbool.h)
#define bool _Bool
#define false 0
#define true 1

なので、stdbool.hをincludeすればbool型としてtrue,falseを使えます。


9 演算の優先順位と結合規則
演算の優先順位は、ほぼBASICと同じ。しかし、Cに特有の演算子(アドレス演算子、間接演算子
シフト演算子など)があるので、一応確認して下さい

優先順位 演算子 結合規則
( )  [ ]  ->  .(構造体、共用体のメンバの指定) −>
  !  ++  --  *(間接演算子) &(アドレス演算子) sizeof <−
  *(かけ算) /  % −>
  +  - −>
  <<  >> −>
  <  <=  >  >= (比較の関係演算子) −>
  ==  != −>
  & −>
  ^ −>
  | −>
  && −>
  || −>
  ? :  (条件演算子) <−
  =  +=  などの代入演算子 <−
, −>

Cのソースを見ると、これらC独特の演算子の記述が見られます。
慣れないうちは、「はっー、なんだこれは」と思うことがしばしばではないかと思います。
BASICから見ると、かなり奇異な記述に見えます。数学の通常の記述からも遠く離れた記述が
しばしば見られます。おじさんも、最初の頃他の人のソースを見て、よく画面の前で固まってしまい
ましたっけ。
要は慣れですから、いくつか奇異な記述の例を解説してみますので、じっくりと見てください。

a = b = c = 0
全て代入ですが、代入の結合規則は右から左ですから、まず c=0 が実行され、ついで
b = c, a = b が実行されます。結果的には全ての変数に0が代入されます。

x = a == b
a と b の比較が行われ、等しければ真(=1)が、等しくなければ偽(=0)が xに代入されます

a = x<y ? y:x
条件演算子がまず実行され、yがxより大きければyの値が、xがyより大きいか等しければxの値が
aに代入される。つまり、x, yの大きい方の値がaに代入される。
BASICで言うと a = max(x,y)のような働き。

if ((c = getch( )) == 0x41) { }
(  )がついているので、分かりやすいですが、cにキーボードから押されたキーコードが代入され
それが、0x41(すなわち半角"a"のキー)かどうか判定している。



10 プログラム例
それでは、最後にCらしい演算子の記述をした(と言うことは、すなわちわかりにくい?)プログラムの例を
2つ程のせておきます。簡単なプログラムなので、解説つけませんので、上の演算子の説明をみながら、
理解してください。

(1) Length.c
これは入力した文字数を返すプログラムです。1文字だけ入力するとプログラムが終了します。
sleng関数のwhile(*s++)は何をしているのかと言うと、*s(入力された文字データ)が0(Null)で無い限り
ループし、n(文字数を入れるカウンタ)を1づつ増加させます。*sが0(すなわち、入力文字列の最後)
になったら、nの値を返して終了です。

/*Coded by Tsuyoshi Kasai*/
#include "stdio.h"

main()
{
 int n;
 char astr[50];
 while(1){
 scanf("%s",astr);
 n=sleng(astr);
 printf("length----%d\n",n);
 if( n==1) break;
 }
 }
 sleng(s)
 char *s;
 {
 int n=0;
 while(*s++) n++;
 return(n);
 }

実行例

C:\C>length           
abcd
length----4
1234567
length----7
q
length----1

C:\C>

(2) ascdisp.c
押されたキーのアスキーコードを2進、10進、16進数で表示するプログラム。
これは、シフト演算子の使用例です

/*  演算子使用例(アスキーコード表示)  coded by Tsuyoshi Kasai for LSIC  */

#include <stdio.h>

void ascdisp(int);

main(){
int c;
printf("演算子使用例(アスキーコード表示)coded by Tsuyoshi Kasai for LSIC");
printf("\n終了はリターンキー\n");
while(1){
if ((c=getch())!=13) ascdisp(c);
    else break;
}
}

void ascdisp(int c){
int i,bit;
printf("\n %c アスキーコード10進数: %d  16進数:%x ",c,c,c);
/* 2進数に変換 */
printf("  2進数: ");
for (i=7;i>=0;i--){
     bit=(c>>i)&0x01;
     printf("%1d",bit);}
printf("\n");
}

実行例

C:\C>ascdisp
演算子使用例(アスキーコード表示)coded by Tsuyoshi Kasai for LSIC    
終了はリターンキー

1 アスキーコード10進数: 49  16進数:31 2進数: 00110001

2 アスキーコード10進数: 50  16進数:32 2進数: 00110010

$ アスキーコード10進数: 36  16進数:24 2進数: 00100100

A アスキーコード10進数: 65  16進数:41 2進数: 01000001

V アスキーコード10進数: 86  16進数:56 2進数: 01010110

` アスキーコード10進数: 96  16進数:60 2進数: 01100000

@ アスキーコード10進数: 64  16進数:40 2進数: 01000000

C:\C>


というわけで、毎度のことながら盛りだくさんの内容となってしまいましたが、比較的理解しやすい
部分だったので、わりとすんなりいけたと思います。いかがだったでしょうか

TopPage