第14章 その他の技術・1

1 マクロ

教科書P198〜203

1-1 単純なマクロ

例えば次のような配列を扱うプログラムがある。

#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 記号定数名 文字列

[機能]

[POINT]

練習問題

教科書199ページ練習問題44

1-2 引数付きマクロ

マクロに引数を持たせることにより、関数のように使うことができる。

《例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 マクロ名(仮引数並び) 仮引数を含むマクロ定義本体

練習問題

#define heiho1(x) x * x
#define heiho2(x) (x) * (x)
    ・
    ・
    ・
c = heiho1(5 + 5);
d = heiho2(5 + 5);

のとき、変数c、dの値を答えよ。

1-3 マクロと条件演算子を組み合わせる

マクロは引数を持て、関数のように扱えることがわかった。であれば、条件演算子を利用して、条件により変える値を変化させることもできる。

《例》絶対値を求めるマクロ

#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));

}

練習問題

教科書201ページ練習問題45

2 前置演算と後置演算

教科書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

《解説》

  1. cnt1++は後置演算のため、printfが実行されてから++が行われる
  2. ++cnt2は前置演算のため、++が行われてからprintfが実行される
  3. for文が終わった後では演算が終了しているため、cnt1もcnt2もどちらも同じ値になる。

練習問題

教科書147ページ練習問題34

3 enum型

教科書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)
    {
        case left:
            左が押されたときの処理;
            break;
        case right:
            右が押されたときの処理;
            break;
        case up:
            上が押されたときの処理;
            break;
        case down:
            下が押されたときの処理;
            break;
        default:
            何も押されなかったときの処理;
            break;
    }
    ・
    ・
    ・

《POINT》

マクロ(define文)と同じような使い方ができるが、使い方次第ではより効率的なプログラムを作れる。

練習問題

教科書177ページ練習問題41

4 共用体

教科書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);

}

《解説》

《補足》

実際の変数の大きさを見てみよう。宣言した変数のメモリ上のサイズを調べるには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 ←になるはずだが、実際はそれよりも大きくなる

[ TOP ]