教科書P198〜203
例えば次のような配列を扱うプログラムがある。
#include <stdio.h> void init_tbl(int *); /* 配列初期化 */ void main(void) { int tbl[50]; int i, j; init_tbl(tbl); for (i=0 ; i<50; i++) { ・ ・ ・ } void init_tbl(int t[50]) { int i; for (i=0 ; i<50; i++) t[i] = 0; }このように、配列の個数をプログラム内に複数埋め込んで使用するようなプログラムは、配列の数を変更したいときに、メンテナンスが面倒である。
そこで、配列の個数をマクロで定義することにより、メンテナンスがしやすいプログラムを作ることができる。
#include <stdio.h>#define YOUSO 50← セミコロンがつかないことに注意!! void init_tbl(int *); /* 配列初期化 */ void main(void) { int tbl[YOUSO]; int i, j; init_tbl(tbl); for (i=0 ; i<YOUSO; i++) { ・ ・ ・ } void init_tbl(int t[YOUSO]) { int i; for (i=0 ; i<YOUSO; i++) t[i] = 0; }このようにプログラムを作ると、要素数に変更があった場合でも
define文で定義した数を変更するだけで修正できる。[マクロの書式]
#define 記号定数名 文字列 [機能]
- コンパイル時にソース内から#defineで定義されている「記号定数名」を探し、対応した「文字列」に置き換える。
[POINT]
- 「#define」と「記号定数名」、「記号定数名」と「文字列」の間は1つ以上の空白又はタブで区切る。
- 通常の変数と見た目で区別するため、記号定数名は大文字で記述する(推奨)。
- マクロは1行にしか書けない
練習問題
教科書199ページ練習問題44
マクロに引数を持たせることにより、関数のように使うことができる。
《例1》理想体重を求める関数をマクロで作る
#include <stdio.h>#define man(x) ((x) - 110)#define lady(x) ((x) - 100)void main(void) { int sincho; printf("あなたの身長を入力してください:"); scanf("%d", &sincho); printf("男性は %dkg 女性は %dkg が理想体重です。\n",man(sincho),lady(sincho)); }《解説》
プログラム中で
man(sincho)とマクロコールすると、プリプロセッサはこれを((sincho) - 110)と展開し、プログラム中のman(sincho)と置き換える。《構文》
#define マクロ名(仮引数並び) 仮引数を含むマクロ定義本体
- マクロ名と( )の間に空白を置いてはいけない。
- 一般にマクロ定義本体に書く引数は(計算の優先順位の)安全のために( )で囲む。
- マクロの引数は型を持たないのでどんな型にも対応できる。
(帰ってくる値の型が保障できない)- マクロ展開はその位置にマクロ本体を埋め込むため、オブジェクトサイズ(実行可能ファイルの大きさ)は大きくなるが、実行時の関数コールによるオーバーヘッドはない。
- マクロは1行に書ける範囲に限られるため、複雑な処理は行えない。
(¥を行末において次の行に継続することはできるが・・・)練習問題
#define heiho1(x) x * x #define heiho2(x) (x) * (x) ・ ・ ・ c = heiho1(5 + 5); d = heiho2(5 + 5);のとき、変数c、dの値を答えよ。
マクロは引数を持て、関数のように扱えることがわかった。であれば、条件演算子を利用して、条件により変える値を変化させることもできる。
《例》絶対値を求めるマクロ
#include <stdio.h>#define ABS(x) (((x) > 0) ? (x) : -(x))void main(void) { int num; scanf("%d", &num); printf("%dの絶対値は%d\n", num,ABS(num)); }
- シンプルな関数ならマクロで書くことができる。
マクロではデータの型が保障されないため、例のマクロの場合、int型のデータでもdouble型のデータでも使うことができる。練習問題
教科書201ページ練習問題45
教科書P146〜147
インクリメント演算子(++)とデクリメント演算子(−−)は、変数の前と後のどちらにも置けるが、前に置くか後に置くかによって演算結果に違いが出る。
《例》前置き演算と後置き演算の違い
#include <stdio.h> void main(void) { int i, cnt1 = 0, cnt2 = 0; printf("cnt1++ ++cnt2\n"); for (i=0 ; i<5 ; i++) printf("%6d %6d\n",cnt1++,++cnt2); printf("for文終了後\n"); printf("%6d %6d\n", cnt1, cnt2); } [実行結果] cnt1++ ++cnt2 0 1 1 2 2 3 3 4 4 5 for文終了後 5 5《解説》
- cnt1++は
後置演算のため、printfが実行されてから++が行われる。- ++cnt2は
前置演算のため、++が行われてからprintfが実行される。- for文が終わった後では演算が終了しているため、cnt1もcnt2もどちらも同じ値になる。
練習問題
教科書147ページ練習問題34
教科書P176〜177
データの取りえる値を全て列挙することによって定義される型を
列挙型(enum型)という。意味の希薄な数値データを意味のある定数名として割り付けることにより、プログラムを見やすくする効果がある。《enum型の宣言》
enum 型名 {定数名1, 定数名2, ・・・, 定数名n}; 列挙される名前は
int型の値を持つ定数になり、宣言されている順序に 0, 1,・・・, n と連番が振られる。《enum型を持つ変数の宣言》
enum 型名 変数名; enum型で宣言された変数は、
列挙された名前を直接代入できる。《使用例》
#include <stdio.h>enum keytbl {left, right, up, down};int keycheck(void); void main(void) {enum keytbl key; /* キー情報の取得 */ key = keycheck(); switch (key) { caseleft: 左が押されたときの処理; break; caseright: 右が押されたときの処理; break; caseup: 上が押されたときの処理; break; casedown: 下が押されたときの処理; break; default: 何も押されなかったときの処理; break; } ・ ・ ・《POINT》
マクロ(define文)と同じような使い方ができるが、使い方次第ではより効率的なプログラムを作れる。
練習問題
教科書177ページ練習問題41
教科書P168〜170
共用体は、
複数の異なる型のデータを同一の領域で共用するデータ型である。《共用体の宣言》
union 共用体タグ名 { データ型 メンバ名; データ型 メンバ名; ・ ・ };《共用体変数の宣言》
union 共用体タグ名 共用体変数名; 《構造体と共用体の違い》
構造体 共用体 変数の宣言 struct _sbody {
char c;
int num;
long score;
} sbody;union _ubody {
char c;
int num;
long score;
} ubody;メモリの状態 説明 3つの変数(メンバ)に必要な領域(1+2+4=7バイト)が確保される 3つの変数(メンバ)のうち、一番大きな変数の大きさ(4バイト)が確保される 特徴 宣言したメンバを同時に使用できる 宣言したメンバのうち、どれか1つしか利用できない 《プログラム例》
#include <stdio.h> typedef union _uBODY { char c; int num; long score; } uBODY; void main(void) { uBODY body; /* メンバ「c」を使う */ body.c = getchar(); printf("body.c = %c\n", body.c); /* メンバ「num」を使う */ scanf("%d", &body.num); printf("body.num = %d\n", body.num); /* メンバ「score」を使う */ scanf("%ld", &body.score); printf("body.score = %ld\n", body.score); }《解説》
- 共用体のメンバはそれぞれ名前は違うが同じメモリ領域に宣言されているため、単体でしか使うことができない。
- メンバcにデータ格納後、メンバnumにデータを格納すると、メンバcのデータは消えてしまう(上書きされる)。
《補足》
実際の変数の大きさを見てみよう。宣言した変数のメモリ上のサイズを調べるには
sizeof関数を使う。この関数は、引数で与えられた変数又はデータ型のバイト数を返す。
《プログラム》 #include <stdio.h> typedef union _uBODY { char c; int num; long score; } uBODY; typedef struct _sBODY { char c; int num; long score; } sBODY; void main(void) { printf("char の大きさ:%d\n", sizeof(char)); printf("int の大きさ:%d\n", sizeof(int)); printf("long の大きさ:%d\n", sizeof(long)); printf("double の大きさ:%d\n", sizeof(double)); printf("共用体変数 ubody の大きさ:%d\n", sizeof(ubody)); printf("構造体変数 sbody の大きさ:%d\n", sizeof(sbody)); } 《実行結果》 char の大きさ:1 int の大きさ:2 long の大きさ:4 double の大きさ:8 共用体変数 ubody の大きさ:4 構造体変数 sbody の大きさ:7←になるはずだが、実際はそれよりも大きくなる