演算に関しては、C も BASICもそう大きな違いはないようです。
四則演算はCもBASICも、+ - * / を演算子として使います。
しかし、C独特の部分や微妙に違っていたりする部分もあります。さしあたって、BASICと
異なる部分を中心に取り上げてみます。
なお、このページでは取り上げなかったものとしてcast演算子や、データ型が混在した計算における
データ型と精度の問題等があります。これについてはcast演算子とオートキャストをご参照下さい。
また、三角関数や指数関数、logといった算術関数については算術関数をご参照下さい。
1 演算子記号の違い
以下の演算子記号がBASICとCで違います
多くのBASIC | C | |
剰余 | mod | % |
べき乗 | ^ | 関数 pow() |
等しい | = | == |
等しくない | <> | != |
論理積 | and | && |
論理和 | or | || |
否定 | not | ! |
べき乗はBASICでは ^ を使いますが、 ** を使うプログラム言語が多いです(Fortran,
Perl, Ruby, Java Script, PHPなど)。
Cにはべき乗の演算子はなく、math.h にあるpow()関数を使います(※)。
pow(X,n)はXnを表わします。
条件判定で等しい場合は==を使います。他のプログラム言語では=を使うものが多いので
ミスしやすい個所です。
(※)べき乗の計算に関数pow()を使うプログラミング言語は他にもJava, Go,
Kotlinなどがあります。
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
(例) a = 0x7 x = a<<2 −−>aを2ビット左シフト 0x7 : 00000111 00011100 -->0x1cになる
代入演算子 | 普通の記述 |
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"のキー)かどうか判定している。
他に、間接演算子とインクリメント、デクリメント(→インクリメント、デクリメント演算子参照)がからんだ例では、
*++n, ++*n, *n++ などどういう、どういう優先順位で演算が実施されるのかよくわからない式があります。
これらの例については「演算子の優先順位の罠」に説明がありますのでご参照下さい。
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> |
というわけで、毎度のことながら盛りだくさんの内容となってしまいましたが、比較的理解しやすい
部分だったので、わりとすんなりいけたと思います。いかがだったでしょうか