<基本編>
C言語の研究1
第1章 基本事項
1 まず何からはじめるのか
今まで、Visual BasicとかDelphi等でWindowsプログラムを組んだことのある人や、COBOLとかBASICでプログラムを作成した人が、C++によるWindowsプログラムにいきなり挑戦して、アウトであったという経験は全くなかっただろうか?また、全くの初心者の場合は、一体どうすればいいのだろうか。
今お使いの環境は、WindowsだろうがUNIXだろうが、しばらくの間、これを単にOS、すなわちオペレーティングシステムとして、人とマシンの仲介役(インターフェースという)をしているにすぎない、つまりC言語をマスターすることとは直接関係がないと考えていただきたい。
たとえば、Windows上で動くアプリケーションを開発する場合は、C言語で作成するには、OSの一部を構造体等としてポインターで呼び出して、処理していかなければならず、"Hello world"と画面に表示するだけのプログラムでも、相当のコーディングが必要である。
そのため、クラスという概念で必要部分を継承、派生させ、少ないコーディング量でWindowsプログラムを書くのがC++である。
しかし、C++を理解するには、その基本であるC言語が分かっていないと、何のことだか "I can not"という惨めな結末を迎えることだろう。
しかも先ほども言ったように、C++はいまだ規格の定まっていない言語でもある。
したがって、これからC言語そしてC++の必要部分をマスターするまでは、WindowsプログラミングはVisual BasicやDelphi等の高級言語を使って作成し、C++でWindows等のプログラミングが可能になるために、Visual C++で MS DOSなどで動くプログラムを作成して研究することにしよう。
C言語の基本さえしっかりマスターしていれば、C++は簡単に理解できる。その上で、ANSI C/C++によるWindowsプログラミングや、いま脚光を浴びつつあるJavaなどにとりかかれば、赤子の手を捻るも同然に理解できるし、作成能力も一段と増すから本当に不思議なものだ。
また、C言語を研究しながら、並行して、Visual BasicやDelphiでWindowsプログラムを作成して行くことは大いに歓迎する。なぜならば、Visual Basicなどは実践的プログラミングが楽にできるし、実にUsefulであり、C言語と並行した勉強はコンピュータ全体の理解を伸ばすことにつながるからだ。
2 プログラムの基本
(1) 定数と変数
定数(Constants)とは、0(ゼロ)、100、3.14とかの数値、または'A'や'a'の文字定数であり、最初からその値が決定されており、絶対に変化しないものをいう。
これに対して、変数(Variable)とは、その値が決まっておらず、自由に変化させられるものをいう。ただし、変数はまず最初に整数か浮動小数点か、はたまた文字定数の変数なのかを宣言しておく必要がある。
(2) 問題の提起
次の例によりプログラミングの感触をつかんでいただきたい。
(例)
1つの整数型の変数 a を宣言する。 → int a;
次に、この変数aの初期値をゼロに設定する。→ a=0;
aに100を代入する。→ a=a+100;
aを表示する。→ printf("%d\n",a);
この例で、早くも3つの疑問点が生じたことでありましょう。
@まず、int a;のintとは何か
Aa=a+100;は右辺値と左辺値が、0=100という関係になって数学的におかしいのではないか。
Bprintf("%d\n",a);のprintfとは?、そして%d\nとは一体何か?
ここに、これから学ぶべきことが提起された。
@は、データ型
Aは、プログラミング独自の式の表記方法
Bは書式付き出力の方法
次項3から、この3点について研究して行こう。
3 データ型
前項で、下のように変数 aを宣言した
1つの整数型の変数 a を宣言する。 → int a;
それでは、データの型とは一体どんなものか見て行くことにしよう。
(1) 数値型
数値には、@ 整数と A 実数 の2種類があることを小学校で習ったが覚えているだろうか。忘れていてもYou don't have to worry, worry守ってあげたいである。
算数の世界とコンピュータの世界は違う。算数は10進数、しかしコンピュータは2進数で計算をする。
まず最初に、このことを心してかかろう。
コンピュータの世界の数値型には
@整数型〜算数でいう整数 (Integer Type)
A浮動小数点型〜算数でいう実数 しかし、算数とは違って、精度が問題になる (Float Type)
の2種類がある。
A 整数の表現方法
たとえば、コンピュータは2進数で、整数の10をどのように表現しているのだろうか考えてみよう。
2進数には0と1しかない。よって、8ビットマシンの場合、
0は,0000,0000とゼロが8個並ぶ。(区切りは便宜上入れた、実際には入っていない)
1は,0000,0001と表現される。
すると、2になると、0000,0010と桁上がりする。つまり右から2桁目だけがオンになった状態だ。
それぞれの桁は2の階乗(べき数)だ。つまり、全部の桁がオンになれば、
=255
これは、2の8乗つまり256に1足りない数だ。こういうことだ。
128+64+32+16+8+4+2+1=255
これが、わが8ビットマシンの最大表現整数値だ。
余分な話だが、わが8ビットマシンで表現できる種類は、数字の0(ゼロ)もあるから、256種類ということになる。
さて、それでは 10を表現するのはどうするか。
上の足し算の8と2の部分をオンにすればいい、ということは、0000,1010とすればいい。
53はどうする?32と16と4と1がオンだ、だから、00110101
一番簡単な方法はこうだ。
下から逆に読んで110101となる
上に0を2つ追加してほしい。00110101、これでOK
それでは、マイナスはどうやって表現するの? と君は聞くだろう。いい質問だ。
算数の世界にはプラスとマイナスがある。すなわち、正と負の整数。
この場合は、一番左の桁、つまり128の桁を符号の桁にしなければならないだろう。
0000,0001はプラス1、1000,0001はマイナス1としておこうか。
しかし、この方法では苦しいところがあるぞ。
0000,0000はプラスのゼロ、1000,0000はマイナスのゼロ。
う〜む、ゼロが2種類もあっていいものか、ゼロはゼロじゃ。
そこで2の補数という考え方を採用しよう。
たとえば、1は0000,0001、このマイナス1は
0と1を一旦、反転させる。すると、1111,1110になるね。
こいつに1を加える。すると、1111,1111。これがマイナス1だ。
逆に、さっきの1000,0000はマイナス何かを見るときは、
一旦反転させて、0111,1111
これに1を加えると1000,0000つまり、128 、マイナス128を意味する。
この符号付き整数は、わが8ビットマシンでは、今の-128から0111,1111つまりプラス127までしか表現できないのだ。
そんなことじゃ、何か、89509999×96854をどうやって計算するんだ。電卓の方が賢いじゃないか!!
と君は怒るだろう。
そのために、コンピュータは浮動小数点方式をとる。
B 実数の表現方法
実数とは、3.1415とか100.05のように小数点がついている数値のことだった。
3.14について考えてみよう。これは、314×10-2、つまり314に1/100をかけたものに等しい。
これはまた、31.4×1/10としても正解でありますね。
ところで、わが8ビットコンピュータで3.14を表現してみよう。
小数点のある数値を表現するには、次の図のように、10の何乗かを表示するための「指数部」というのを追加しなければならないだろう。
こんな愚かなコンピュータは世の中に存在しないと思うが、こいつで表示できる数値は
-15×103 〜 15×103 までが表現可能である。つまり-15000から15000
しかし、前に述べた精度が問題という理由はここにある。
これで、3.14を表示するにはどうするか。
仮数部が15までしか表現できないため、3.14のうち3を表現するのがやっとこさである。
指数部は、この場合10の0乗であるから、00000011という表示になるだろう。
ということは、3.14-3=0.14 この0.14が桁落ちしている。
これが、精度の問題という理由である。もっとも、多くのコンピュータは32ビット以上で浮動小数点演算を行うので、こんなひどい桁落ちはないので安心してもらいたい。
それで、今は、実数を表現する方法として、3.14を例にとると、eやEが10の指数を表示するものと規定して、
314e-2、314E-2 として表現することと同じだということを覚えておこう。
<覚えておこう>
我々はプログラマであるから、一番左の桁とか、右端の桁という言い方はやめよう。
つまり、一番右の最下位の第0ビット目をLSB(Least Significant Bit)
一番左の最上位の第7ビット目をMSB(Most Significant Bit)と、それぞれ呼ぼう。
そして、符号なしの整数を unsigned integer、略して unsigned int
符号付整数を signed integer、または単にinteger、略して intと覚えておこう
また、小数点のある数値は、float または doubleと覚えておこう。
それで最初の話にもどるが、1つの整数型の変数 a を宣言する。 → int a; としたわけだ。
これを仮に、実数型の変数aを宣言する場合は、→ double a;とすればいい。しかし、この場合の初期値ゼロは a=0.0;とコンピュータに明示しておいてやるとベターだ。
(2) 文字定数型
データ型の大きな分類の1つとして、数値型のほかに「文字定数型」(Character Type)というのがある。
普通使う「コンニチハ」とか「サヨウナラ」というのは、コンピュータにとっては文字ではない。
文字の配列、つまり、文字列である。
特に全角文字の「こ」や「ん」はそれ自体が文字列とみなされてしまうので注意が必要だ。コンピュータはアメリカ人だからだ。
いま、文字列「コンニチハ」を例にとって、これがコンピュータ内でどのように記憶されているか見てみよう。
ひどい図だ! コンニチハかコンニチワかどっちが正しい日本語なのか、とにかく5文字である。
ところが、コンピュータはこれを常に6文字として格納する。文字列の末尾には、必ずいつも、NULL(「ナル」)を格納する。
この 'コ' や 'ニ' といった一つ一つが文字定数で、独立した人格?がある。
言葉をこんな文字定数で表現することは日常生活では少ないことだろう。しかし、コンピュータは人間ではなく、マシン(マシモ?)だから、文字定数を使うこともある。〜なんのこっちゃ?
とにかく、データ型の1つとして、文字定数型があることを覚えておいてほしい。
文字定数型の変数 a の宣言は、character の charをとって→char a;
というように表現する。これに 'コ' を設定するのは、 a='コ';
文字定数はこのようにして扱う。
データの型としては、この他にもあるが、その都度研究することとして、次に移ろう。
4 プログラム独自の表記方法
さて、前に aに100を代入する。→ a=a+100; と書いた。言い方が悪かった。代入ではなく、加えると言ったほうが正確であろう。言葉は難しい。
代入の場合は、a=100; という書き方です。
これはすべてのプログラミング言語で共通の文である。
「すなわち、変数 aに100を加えて a にする。」 a←a+100 というほどの意味でしかない。
コンピュータは算数ではない。これは方程式ではなく指令方法であります。
したがって、勝手にコンピュータが数学の問題を解いてくれることはないのだ。
プログラムか何かで指令してやることが必要なのだ。
また、C言語は簡潔な式が好きだ。 a=a+100は、a+=100と記載しよう。
a=a/100;はa/=100; a=a*100;はa*=100; a=a-10はa-=10; a=a+bはa+=b; といった感じで、どんどん使おう。
<思い出してみよう>
前項で整数の変数aを int a; と宣言した。
この指令はコンピュータに一体どんなことをさせているのだろう。
これは、CPUのメモリ空間に、1つの整数型の変数の領域を確保させるということだ。
処理系によって異なるが、大体8バイトつまり32ビット空間を確保させている。しかし、まだ値を与えていないから、その中身は不定である。aに何が入っているのかわからない。
そこで、整数の100を初めてaの中身として設定するには、a=100; とする訳だが、宣言と初期化を一緒にする方が楽なので、 int a=100; と書いてしまおう。C言語は、面倒くさがりによく似合う言語だ。
これを、 int a = 0.0; と書いたら、Visal Basicは許しても、Visual C++は怒ってコンパイルのとき、警告を発することだろう。
一度宣言したデータ型はきちんと守ろう。
そしてC言語の場合、変数宣言は最初にしておこう。途中で宣言することは許されない。
まず最初に変数宣言をすること。これを守ろう。
5 書式付き出力の方法
さて、何事も結果の確認が大切だ。世の中は結果主義で、いくら努力しても結果を出さないと誰も認めてくれないだろう。
int a=100;
この a の結果を見たくても、画面に出すか、プリンタ出力して確認しなければ、このCプログラミングが果たして正しいのか確認できない。
そのために、わが友 prinft関数 (プリントエフ関数)がある。
printfを使うためには、その前に標準ライブラリ関数のヘッダーファイル、stdio.hを取り込んでおく必要がある。
おっと、また訳のわからないことを言ってしまったかな。これについては、後の章で説明するつもりだ。
このstdio.hには有益な標準で使用するライブラリ関数がprintfを含めて多く含まれている。
したがって、こいつを先にinclude(取り込み)しておこう。
そのために、最初に #include <stdio.h> と記載しよう。
ついでに言うと、C言語はmain関数に始まり、main関数で終了する。
そのパターンは、
#include <stdio.h>
void main(int argc, char **argv)
{
/* ここにコンピュータに対する命令文を記述する*/
int a=100;
printf("a = %d\n",a);
/*命令文の最後には必ずセミコロン つまり、; をつけること */
/*これに囲まれた部分は注釈となるし、
複数行にまたがってもOKだ ワハハハハ*/
}
大体、こんなかたちだ。
さて、printf関数だが、標準ライブラリstdio.hの中では
int printf(const char *format.....)と定義されている。関数については後で詳しく説明することになるが、
戻り値のデータ型 関数名(引数〜ひきすうと読む)
として、必ず戻すデータの型を最初に決めておかねばならない。
printf関数は整数のデータを戻り値としている。
それでは、printf関数の返す値とは何か?それは出力した文字数である。
そして、使い方は
printf("出力する内容",オブジェクトの並び);
または
printf("何んでもいい");
と書く。
printf関数は大切だから徹底的に研究してみよう。覚悟してくれ。
(1) 単なる出力
単なる字を表示するだけなら、printf("Hello World!"); のように " と " の間に好きな文字を書けば画面に、Hello World!のように出力される。
この場合、改行しないから、続けて、printf("こんにちわ");とすると、
Hello World!こんにちわ と改行せずに表示してしまう。
改行をしたければ、\nという「ニューラインコード」と呼ばれる符号を入れることだ。
printf("Hello World!\n");
printf("こんにちわ\n");
これによって、
Hello world!
こんにちわ
と改行されて画面に表示される。
(2) 変換指定出力
先の整数変数aを表示してみよう。
int a=100;
printf("aの値は%dです\n",a);
"と"の間に %d というやつがあるが、この%が変換指定記号とよばれている。つまり、%の後に、以下の指定子を記入してほしい。
dは整数、fは浮動小数点、xは16進数、eは指数部のつく浮動小数点、sは文字列、cは文字定数である。
他にもあるが、%d %f %x %e %s %cを知っていればほとんどOKだ。
これに代入されるのが「オブジェクトの並び」として printf("%d",a);の ,以下の部分だ。この場合はaの値だ。
つまり、printf("aの値は%dです\n",a);と書くと
aの値は100です
と表示される。
別に変数に限らない、定数を,以下に書いてもよい。たとえば、
printf("%d %c\n",100,'m'); とすれば、100 m と表示されることだろう。
しかし、 printf("%f\n",3.14); の場合は注意が必要だ。
このままでは、3.140000と余分な0が、4以下に並んでしまう。また、3.1415.....みたいなやつを小数点以下第2位とかで四捨五入したいときもあるだろう。
こんな場合は、出力の桁数を指定できる。たとえば、
printf("%5.2f",3.1415); と、ピリオドで小数点以下の桁数を指定すれば、3.14と指定の桁以下を四捨五入してくれるから便利だ。
また、こんな書き方もある。 printf("%5.*\n",2,3.14);
.(ピリオド)に続けて*(アスタリスク)を指定すると、*の値つまり精度は、後方の並びの中の対応する値が参照されることになる。これは第二種情報処理試験でよく出題される手だから注意が必要だ。
つぎに16進数で10を表示してみよう。
printf("10を16進数で表示すると、%x",10); こうすると、 a と表示する。
xを大文字にして、printf("10を16進数で表示すると、%X",10); とすると A と表示する。
aやAは16進数の10のことだ。どうだ、おもしろいだろう。
文字列を表示してみるか?
printf("%s","こんにちわ世界"); 文字列は"と"で囲まなければなければならない。
なぜだろう? 前に少し言ったが、文字列は文字定数の配列のことだ。
char a[]="こんにちわ世界";
printf("%s",a);と書くのが普通かも知れない。
配列については、後で説明しよう。
とりあえず、printf("%d",a); の方法で出力することを覚えておこう。
<最初のプログラム>
/* aに100を代入して出力する */
#include <stdio.h>
void main(int argc, char **argv)
{
int a=100; /*まず最初に変数宣言*/
printf("aの値は%.*dです\n",3,a); /*前記の赤字を参照のこと*/
}
6 定数の定義方法
定数はどのように定義すれば楽なのだろうか。
int a=100; では変数aに100を代入した。 これを定数aとして絶対変更できないようにするには、constantの頭をとったconst
これを最初に付けて
const int a=100; とすると、aは定数となる。だから、
const int a = 100;
a=20;
とすると、コンパイラはエラーメッセージを出す。
aは定数で、値を変化させることはもうできないからだ。
この方法のほかに、識別子をマクロとして定義する方法がある。
#define PAI 3.14
と最初に書いておくだけでよい。
これを利用してプログラムを書いてみよう。
#include <stdio.h>
#define TEISU 100 /* 命令文ではないから、文末記号 ; はいらない*/
void main(int argc, char **argv)
{
int a = TEISU; /*そのままマクロ定数を利用*/
printf("%d\n",a);
}
7 配列
配列とは、同じデータ型の変数をまとめて扱うものをいう。
文字列は文字定数の配列だった。
なんでもいいが、たとえば整数の配列は、 int a[5]={1,2,3,4,5}; のように書ける。
データ型指定子 配列名[サイズ] = {初期化子の並び};
ただし、C言語の場合、配列の添字は0から始まるので、これは、a[0]〜a[4]の配列ということだ。
また、二次元配列もある。
たとえば、a[3][5]={ {1,2,3,4,5}, {10,20,30,40,50}, {100,200,300,400,500} };
とでもいう感じだろうか、感覚でつかんでいただきたい。
配列の初期化は後でも出来る。たとえば、
a[5]; と定義しておいて、
a[5]={1,2,3};とすると、a[3]とa[4]は自動的に0で初期化される。
a[3][5]={ {1,2,3}, {10}, {100,200,300,400} };とすると、a[0][3]やa[0][4]、a[1][1]などの初期化されていない部分は0で初期化される。
printf("%d %d",a[0][4],a[0][3]);とでもして試してみると面白い。
8 演算子とプライオリティ(優先順位)
C言語の演算子は、多彩であり、記号じみている。
この部分で何かとっつきにくい感じがするものだが、これらの書き方にはすぐになれるので心配はいらない。
(1) 算術演算子
足し算、引き算、掛け算、割り算はなつかしいことだろう。C言語で表記してみよう。
足し算: 5+2;
引き算: 5-3;
掛け算: 5*2; アスタリスク*を×の代用品とする
割り算: 5/2; スラッシュ/を÷の代用品とする
これらに加えて、C言語には、剰余つまり割り算でいう余りを一発で算出する式がある。それは、
%だ。書式指定子だけではなく、いろいろ利用されているんだね。
剰余算: 5%2; この場合、5/2の余りは1だから、1が算出される。
割り算の場合、被除数と除数つまり5/2のように両方とも整数の場合は、結果は整数である。つまり2。
ところで、これらの四則演算を整数型と実数型のデータを使ったらどうなるか?
たとえば、5+2.2 5-2.2 5*2.2 5/2.2 の場合であるが、解は実数となる。
5/2を実行させて、2.5を表示したい場合であるが、
printf("%5.2f\n",5/2); ではだめ、2.00と表示される。
printf("%5.2f\n",5/2.0); と片一方のオペランドを実数にしておこう。2.50これでOKだ。
また、printf("%d\n",5%2.0);と整数と実数との剰余を求めようとしてはいけない。
%の式は、整数同士の割り算にだけ使用すること。 printf("%d\n",5%2);とすれば1と表示されてOKだ。
(2) ビット演算子
このビット演算子はC言語独特のもので、簡潔にして、用途が広く、情報処理試験のような「落とす」試験では、普通は作成しないような、ことさら分かりにくいプログラムを、問題として多用されるので注意が必要だ。だが、今は基本編なのでビビる必要はない。ざっと流して行こう。
ビット演算子として、以下の6種類がある。
& ビット単位のAND(論理積)
| ビット単位のOR(論理和)
^ ビット単位の排他的論理和
<< ビット単位の左シフト
>> ビット単位の右シフト
~ ビット単位の補数
まず、AND(論理積)から説明して行こう。コンピュータの世界は0と1しかないと以前言ったが、2つの0か1かのどちらかがあったとして、それらの論理的な演算をやってみよう。
ANDの場合、1となるのは両方が1の場合しか許されない。だから、0&0や1&0や0&1は全部0になる。
ORの場合は、どちらか一方が1なら1となる。つまり、0|1 や1|0や 1|1は全部1だ。0|0だけ0になる。
排他的論理和とは、言葉のごとく、どちらかが1の場合だけ1となる。つまり、1^0 と0^1の場合だけ1で、
1^1や0^0は0となってしまう不思議な演算だ。
次にシフトを見よう。2進数は左シフトを一回すると2倍することになり、逆に、右シフトすると1/2になるという性質があり、よく用いられる演算子だ。例をみよう。
a<<b これはaをb回左にシフトするという意味だ。この場合、右の空いてしまったb個のビットには0が入ることになっている。
a>>b これはaをb回右にシフトするという意味だ。この場合、左の空いてしまったb個のビットには0が入ることになっている。
最後に、ビット単位の補数だが、これは単にその0と1をそれぞれ反転するということだ。たとえば、
~aとして、仮にaが1111だったとすると、0000と反転してしまうということだ。今までの演算子と違うのは、いわゆるオペランドを1つしかとらないということ、つまり、a~bという形はとらないで、~aか~bという式しかない。これは普通、1の補数と呼ばれている。
ここは若干ややこしいので、例題プログラムで確認しておいてほしい。
<例題>
#include <stdio.h>
void main(int argc, char **argv) /*この説明も後で、ポインタのところでする*/
{
unsigned int a=0x0f4d; /*0xは16進数表記で、0000111101001101のこと*/
printf("a = %4.4x\n",a); /*aを16進数表記*/
printf("a & 0xfff0 = %4.4x\n",a&0xfff0); /*aの下位4桁を全部0にする*/
printf("a | 0xf000 = %4.4x\n",a|0xf000); /*aの上位4桁を全部1にする*/
printf("a ^ 0x00ff = %4.4x\n",a^0x00ff); /*aの下位8ビットを反転する*/
}
さあ、これをちょっと手元のCコンパイラでコンパイルしてみよう。なに?まだ持っていないって!?
ダメじゃん。早く、Visual C++をインストールしよう。
a=0f4d
a & 0xfff0 = 0f40
a | 0xf000 = ff4d
a ^ 0x00ff = 0fb2
と出力されることを確認してほしい。
(3) 代入演算子
A 単純代入演算子
これは普通の算数と同様の書き方でいい。つまり、
x = a + b;
y= a/b; など、右辺の計算結果を左辺に代入するもの。
B 複合演算子
以前に、C言語は簡潔なのが好きだといって、a/=b; とか a*=b; とか書いたと思うが、まさにこれのことで、
+= -= *= /= %= の5つが算術演算子、
&= |= ^= <<= >>= の5つがビット演算子として、それぞれ使用できる。
右辺値と左辺値を演算した結果を左辺値に代入する意味でしかない。
C 増分演算子と減分演算子
桂小枝氏なら「簡潔にもホドがある」と言われるかも知れない。
たとえば整数変数 i が 1 増加するのを、++i または i++ と書ける。i = i +1;と同じである。
i が 1 減る場合は、i = i - 1;のことだが、--i または i-- と書ける。
この++や--を前に置くのと、後ろに置くのとはえらい違いがあるので要注意だ。
i=10; として、
x =10 * i++ とした場合、10*10、つまり増加前のiの値で計算してから、iを1増加させるので、xは100で、
iは11になる。後置増分という
x = 10 * ++i とした場合、iを先に+1して11にさせてからから計算するので、xは110となる。前置増分という。
減分もこれと同様である。
どうだ、記号みたいなものだろう。早速、テストしてみよう。
<テスト>
#include <stdio.h>
void main(int argc, char **argv)
{
int i=10;
int x1, x2;
x1 = 10 * i++;
printf("x1 = %d i = %d \n",x1 ,i);
i = 10;
x2 = 10 * ++i;
printf("x2 = %d i = %d \n",x2 ,i);
}
いかがだろうか? x1は100、x2は110となって、iは両方とも11となっていることを確認できただろうか。
これはよく配列なんかで、 a[i++]としたり、a[++i]としたりするが、この意味は、
i を10で使って、後で11にしたいのか、〜後置
それとも先に i を11にしてしまってから使いたいのか〜前置
これら全ては、プログラマの意図に左右される。
(4) キャスト演算子
変わったところでは、このキャスト演算子がよく利用される。
(型名) キャスト式
これは、式の形を( )で指定の型に変換するもので、たとえば、整数intを浮動小数点のdoubleに変換(キャスト)したりする。
これは、整数同士の割り算であるが、結果を小数点以下第一位まで四捨五入して求めたい場合などに便利である。この場合、キャストするのはあくまで式だけで、そのオブジェクトは変更されないので注意しよう。
さっそく例題をやって確認することにしよう。
<例題>
#include <stdio.h>
#define KAMOKU 3 /*マクロ定義で科目数を3に指定*/
void main(int argc, char **argv)
{
int kokugo = 90;
int sansu = 85;
int rika = 70;
int total;
double ave;
total = kokugo + sansu + rika;
ave = (double)total/(double)KAMOKU;
printf("平均点は%6.1f点です\n",ave);
}
どうだろうか、「平均点は 81.7点です」と表示されたかな?
(5) sizeof演算子
sizeof(オペランド)
このsizeof演算子は、オペランドのサイズをバイト数で求めるものであります。
自分の処理系で、int long int double charがどれくらいの大きさを食っているか、次の例題で求めてみよう。
<例題>
#include <stdio.h>
void main(int argc, char **argv)
{
int n;
long int ln;
double d;
char c;
printf("int型は%dバイト\n",sizeof(n));
printf("Long int型は%dバイト\n",sizeof(ln));
printf("double型は%dバイト\n",sizeof(d));
printf("char型は%dバイト\n",sizeof(c));
}
いかがだろうか、変数をそれぞれ4つの型を宣言し、そのサイズを表示させた。
変数を宣言したのに、変数としては全然使っていないので、Visual C++は警告を発するかも知れないね。
(6) 関係、論理、条件演算子
C言語のデータ型には、Visua BasicのようなTrue,False型のいわゆる論理型はない。
そのかわり論理値は、もっと簡単に、「真」はつまり正しいときは intの1、「偽」つまり間違っているときは intの0として表現される。
A 関係演算子
2つのオペランドの大小を求める演算子としては、次の6つの演算子がある。
< より小さい
> より大きい
<= より小さいか等しい
>= より大きいか等しい
== 等しい
!= 等しくない
ここらは、スピードアップしていこう。この式の結果は1か0になっているわけだ。
B 論理演算子
2つのオペランドの値を1か0の論理値とみなして論理演算を行うには、次の3つの演算子を利用する。
&& 論理積(AND)
|| 論理和(OR)
! 論理否定(NOT)〜単体のオペランドで使用する
ここらの説明は、ビット演算子と同じだから分かるだろう。
C 条件演算子
これもC言語独特の式で、
オペランド1 ? オペランド2 : オペランド3
これは、オペランド1を見て、0でない場合はオペランド2の式の値
0の場合はオペランド3の式の値
をそれぞれ返すものだ。さっそくテストしてみよう。さっきキャスト演算子の例題を利用して、80点以上は合格の科目で、結果に * を表示させるように例題を変えてみよう。
<例題>
#include <stdio.h>
#define KAMOKU 3
void main(int argc, char **argv)
{
int kokugo = 90;
int sansu = 85;
int rika = 70;
int total;
double ave;
char hoshi; /* 80点以上の科目は*印をつける
*/
hoshi = (kokugo >=80) ? '*' : ' ';
/* 条件演算子 */
printf("国語の結果は%3d
%1c\n",kokugo,hoshi); /* 80点以上なら*を表示*/
hoshi = (sansu >=80) ? '*' : ' ';
/* 条件演算子 */
printf("算数の結果は%3d
%1c\n",sansu,hoshi); /* 80点以上なら*を表示*/
hoshi = (rika >=80) ? '*' : ' ';
/* 条件演算子 */
printf("理科の結果は%3d
%1c\n\n",rika,hoshi); /* 80点以上なら*を表示*/
total = kokugo + sansu + rika;
ave = (double)total/(double)KAMOKU;
printf("平均点は%6.1f点です\n",ave);
}
どうかな? 国語、算数に*が表示されたかな。
(7) 演算子の優先順位
さて、算数では、掛け算(×)や割り算(÷)を、足し算(+)や引き算(-)よりも先に計算することはよくご存知のところだろう。これが優先順位(priority)が高いということで、C言語でも同じ意味である。
C言語で一番優先順位が高いのは、配列の[ ]、関数の( )、構造体ポインタの->であります。
続いて、いま見てきたところの、++や --、および キャスト演算子やsizeofなどがあり、
その次にから、* / % + - ・・・と各種演算が続く。
一番優先順位が低いのは、カンマ(,)で、続いて低いのが、=(イコール)をはじめ、*= /= %= += -= <<= などの複合代入演算子となっている。