ポインタ⑦(構造体とポインタ) 解答ページ | Programming Place Plus C言語編 第37章

トップページC言語編第37章

問題①

問題① 「パディングの調整」の項で見た、構造体の2つの形式について、自分の環境では各メンバがどのように配置されるか、offsetofマクロを使って確認してください。


たとえば、次のようなプログラムを書きます。

#include <stddef.h>
#include <stdio.h>

struct Data_tag {
    char   a;
    int    b;
    char   c[20];
    double d;
    short  e;
};

struct Data2_tag {
    double d;
    int    b;
    short  e;
    char   c[20];
    char   a;
};

int main(void)
{
    printf( "%u %u %u %u %u\n",
        offsetof(struct Data_tag, a),
        offsetof(struct Data_tag, b),
        offsetof(struct Data_tag, c),
        offsetof(struct Data_tag, d),
        offsetof(struct Data_tag, e)
    );
    printf( "%u %u %u %u %u\n",
        offsetof(struct Data2_tag, d),
        offsetof(struct Data2_tag, b),
        offsetof(struct Data2_tag, e),
        offsetof(struct Data2_tag, c),
        offsetof(struct Data2_tag, a)
    );
}

実行結果:

0 4 8 32 40
0 8 12 14 34

問題②

問題② 2次元上の5つの点を結んで、循環する経路を作りたいと思います。自己参照構造体を使って、このような構造を表現してください。


とりあえず、2次元上の点を表現する構造体型を考えます。x座標と y座標があれば十分でしょう。型の指示は特にないので、int型としておきます。

struct Point_tag {
    int x;
    int y;
};

点は5つあるということなので、この構造体型の変数が5つ、あるいは要素数が5の配列があればよさそうです。たとえば、配列なら次のようにしておけば良いでしょう。

struct Point_tag points[5] = {
    { 1, 2 },
    { 2, 2 },
    { 3, 4 },
    { 5, 4 },
    { 5, 6 }
};

さて、問題は循環する経路ということで、自己参照構造体を使えということでした。つまりは、ある点の「次の」点を表現できれば良い訳です。構造体に、「次の点」を表現するポインタを追加します。

struct Point_tag {
    int x;
    int y;
    struct Point_tag* next;
};

next に、ほかの点のメモリアドレスを格納すれば、次の点をたどれます。5つ目の点の next を、1つ目の点とすれば「循環」させることもできます。

配列 points の初期化子の追加はしてもいいですが、しなくても大丈夫です。メンバの個数に対して、初期化子の個数が少ない場合は、不足分はデフォルトの値で初期化がなされます(第26章)。ポインタ型の場合は、ヌルポインタが入るので、いったんそれで構わないでしょう。

あとは、各点の next を埋めてやります。適当に接続順を決めて試してみます。

#include <stdio.h>

struct Point_tag {
    int x;
    int y;
    struct Point_tag* next;
};

void printPoints(const struct Point_tag* head)
{
    const struct Point_tag* p = head;

    do{
        printf( "%d %d\n", p->x, p->y );
        p = p->next;
    }while( p != head );
}

int main(void)
{
    struct Point_tag points[5] = {
        { 1, 2 },
        { 2, 2 },
        { 3, 4 },
        { 5, 4 },
        { 5, 6 }
    };

    points[0].next = &points[3];
    points[3].next = &points[2];
    points[2].next = &points[4];
    points[4].next = &points[1];
    points[1].next = &points[0];

    printPoints( &points[0] );
}

実行結果:

1 2
5 4
3 4
5 6
2 2

points[0] を先頭として、0 -> 3 -> 2 -> 4 -> 1 -> 0 とつながるようにしてみました。

確認のために、printPoints関数を作ってあります。これは、先頭の点のメモリアドレスを渡すと、そこから点をたどって、座標を出力していくものです。経路は循環しているので、いつか自分自身のところに戻ってきますが、戻ってきたら終わりになるようにしてあります(ヌルポインタが混ざっていたときの対処はしていませんが、してみてもいいでしょう)。

このプログラムのように循環する構造を、循環リストと呼びます。これは、プログラミングをしているとよく登場する典型的な構造の1つです。詳細は、アルゴリズムとデータ構造編【データ構造】第4章で扱っています。



参考リンク


更新履歴

’2018/6/1 内容のすべてを第38章へ上書き。この章の内容は完全に新規のものになった。
章のタイトルを変更(「ポインタ⑦(関数ポインタ)」->「ポインタ⑦(構造体とポインタ)」)

’2018/5/31 第37章の内容をそのままの形で移動して上書き。

’2018/3/13 全面的に文章を見直し、修正を行った。
章のサブタイトルを変更(高度な使用法 -> 関数ポインタ)

’2018/3/9 const に関する練習問題①を、第33章へ移動。

≪さらに古い更新履歴を展開する≫



第37章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ


先頭へ戻る