🇯🇵 日本語 | 🇺🇸 English

第37回 確認問題(構造体)

構造体の初期化、代入、アロー演算子、構造体配列の理解度を確認しましょう。

確認問題1 ― 構造体の基本

struct Point { int x; int y; };
struct Point p = {3, 7};
printf("(%d, %d)\n", p.x, p.y);

出力結果は?

(3, 7)
(7, 3)
コンパイルエラー
(0, 0)
解説: 構造体は宣言順にメンバが初期化されます。p.x=3, p.y=7。

確認問題2 ― 構造体の代入

struct Point a = {1, 2};
struct Point b;
b = a;
b.x = 10;
printf("a.x=%d, b.x=%d\n", a.x, b.x);

出力結果は?

a.x=10, b.x=10
a.x=1, b.x=10
コンパイルエラー
a.x=1, b.x=1
解説: 構造体の代入は値コピー。b は a のコピーなので、b.x を変更しても a.x は変わりません。

確認問題3 ― アロー演算子

struct Point p = {5, 8};
struct Point *ptr = &p;
printf("%d\n", ptr->y);

出力結果は?

5
8
コンパイルエラー
アドレス値
解説: ptr->y(*ptr).y と同じ。ポインタ経由でメンバにアクセスする際はアロー演算子を使います。p.y = 8

確認問題4 ― 構造体配列

struct Student { char name[20]; int score; };
struct Student s[3] = {
    {"Taro", 80}, {"Hanako", 95}, {"Jiro", 70}
};
int max = 0;
for (int i = 0; i < 3; i++) {
    if (s[i].score > s[max].score) max = i;
}
printf("%s\n", s[max].name);

出力結果は?

Taro
Hanako
Jiro
コンパイルエラー
解説: 最大スコアの学生を探すループ。s[1].score=95 が最大なので max=1、s[1].name = "Hanako"

確認問題5 ― typedefでの定義

typedef struct {
    int x;
    int y;
} Point;

Point p = {3, 4};
printf("%d\n", p.x + p.y);

出力結果は?

コンパイルエラー
7
12
34
解説: typedef struct { ... } Point; は構造体に Point という別名を付けます。
  • 以降、struct ... と書かずに Point p; だけで宣言できる
  • p.x = 3, p.y = 4
  • 3 + 4 = 7
typedef を使うと可読性が上がるので、大規模なプロジェクトでは typedef を付けて使うことが多いです。

確認問題6 ― ネストした構造体

struct Point { int x; int y; };
struct Line {
    struct Point start;
    struct Point end;
};
struct Line l = {{0, 0}, {3, 4}};
printf("%d\n", l.end.x);

出力結果は?

0
3
4
コンパイルエラー
解説: ネストした構造体のメンバーにはドットを重ねてアクセスします。
  • l.end は {3, 4}(Point 構造体)
  • l.end.x = 3、l.end.y = 4
複雑な型を表現するときに、構造体をネストさせるのは典型的な手法です。

確認問題7 ― 構造体のサイズとpadding

// 一般的な64bit環境(int=4, char=1, アラインメント4)
struct S {
    char c;
    int i;
};
printf("%lu\n", sizeof(struct S));

出力結果として最も可能性が高いのは?

5(1 + 4)
8(パディングで調整)
4
2
解説: 構造体のサイズは単純な「メンバの合計」ではなく、アラインメント(整列要件)を満たすようパディングが挿入されます。
  • char c(1バイト)+ 3バイトのパディング + int i(4バイト)= 8バイト
  • int は通常 4バイト境界に揃える必要があるため、c の後に詰め物が入る
メンバを大きい型から順に並べると、パディングを減らしメモリ効率を上げられます。

確認問題8 ― 関数への値渡し

struct Point { int x; int y; };

void reset(struct Point p) {
    p.x = 0;
    p.y = 0;
}
int main(void) {
    struct Point pt = {5, 10};
    reset(pt);
    printf("%d %d\n", pt.x, pt.y);
    return 0;
}

出力結果は?

0 0
5 10
0 10
コンパイルエラー
解説: 構造体も関数には値渡し(コピー)されます。
  • reset の中の p は、pt のコピー
  • コピーの値を変更しても呼び出し側の pt には影響しない
  • よって pt は 5 10 のまま
関数内で元の構造体を変更したい場合は、ポインタ渡しvoid reset(struct Point *p))にして p->x = 0; のようにします。構造体が大きい場合、コピーのコストを避けるためにもポインタ渡しが有効です。

確認問題9 ― 構造体の初期化

struct Point { int x; int y; int z; };
struct Point p = {5};
printf("%d %d %d\n", p.x, p.y, p.z);

出力結果は?

5 5 5
5 0 0
5 不定値 不定値
コンパイルエラー
解説: 構造体の初期化子が一部しか指定されない場合、残りは 0 で埋められます(配列と同様)。
  • p.x = 5(明示)
  • p.y, p.z は省略されたので 0
struct Point p = {0}; と書くと全メンバを 0 で初期化できます。指定子付き初期化(C99)では {.x = 5} と特定メンバだけ指定することもできます。

結果

回答してください
構造体の講座に戻るホームへ
← 前の講座
第36回 構造体(struct)
次の講座 →
第38回 共用体(union)

この講座の理解を深めるおすすめ書籍

サイトで動きを理解し、書籍で演習量を補うと効果的です

📘
苦しんで覚えるC言語
MMGames 著
初心者向けの定番入門書。
Amazonで見る
📗
新・明解C言語 入門編
柴田望洋 著
図解が豊富で演習問題も充実。
Amazonで見る
📙
プログラミング言語C 第2版
B.W.カーニハン, D.M.リッチー 著
通称K&R。C言語の原典。
Amazonで見る

※ 上記リンクはアフィリエイトリンクです。購入によりサイト運営を支援いただけます。

この記事をシェア
X(Twitter)でシェア Facebookでシェア LINEで送る はてブ