0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クラウド開発環境PaizaCloudクラウドIDEでHello World(C言語ポインタ 発展編)

Last updated at Posted at 2025-10-08

はじめに

以下の前回の記事では、変数や配列のアドレスを printf("%p") で出力しながら、ポインタの基本を体験しました。
クラウド開発環境PaizaCloudクラウドIDEでHello World(C言語ポインタ 導入編)

今回はその発展編として、構造体をレコード形式で管理し、realloc を使って可変長リストを扱う方法を紹介します。

これは実務でもよく出てくるテーマで、データベースのように「レコードを順次追加して管理する」といった場面に直結します。

サンプルコード

今回のサンプルコードです。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 学生レコードの構造体
typedef struct {
    int id;
    char name[50];
    int age;
} Student;

int main(void) {
    int count = 0;
    Student *students = NULL;  // 動的配列(最初はNULL)

    // 1人追加
    count++;
    students = realloc(students, count * sizeof(Student));
    students[0].id = 1;
    strcpy(students[0].name, "Taro");
    students[0].age = 18;

    // さらに追加
    count++;
    students = realloc(students, count * sizeof(Student));
    students[1].id = 2;
    strcpy(students[1].name, "Hanako");
    students[1].age = 19;

    // 出力
    for (int i = 0; i < count; i++) {
        printf("%d: %s (%d)\n", students[i].id, students[i].name, students[i].age);
    }

    free(students);
    return 0;
}

実行結果(例)

1: Taro (18)
2: Hanako (19)

今回のサンプルコードのポイント解説

  • 構造体で1レコードを定義
    → Student 型を定義して、id・name・age をまとめて管理できます。
  • 動的配列(Student)を使う
    → 最初は NULL で用意しておき、realloc でサイズを増やします。
  • realloc の特徴
    • 元データを保持したまま、領域を拡張できます。
    • ただし内部的に別の場所へコピーされることもあるので、必ず students = realloc(...) のように「戻り値を再代入」する必要があります。
  • 最後は free で解放
    → 動的に確保したメモリは free で解放しないとメモリリークにつながります。

ここまでのまとめ

malloc + realloc を組み合わせることで 可変長のリスト を実現できる

  • 構造体と組み合わせると、実務的なレコード管理に直結する
  • 今後はこれを応用して「リンクリスト」「木構造」など、より複雑なデータ構造へ発展させることができる

reallocに関する補足

ここでの realloc は便利ですが、大規模なデータを何度も拡張する場合にはオーバーヘッドが発生します。実務では「ある程度まとめて確保」して効率化する設計もよく使われます。
まずは仕組みを理解するために、小さな例で試すのがオススメです。

ポインタ演算版でのサンプルコード

次はさらに一歩進めて、ポインタ演算だけで同じ処理を書く方法を紹介します。

C言語では配列とポインタが密接に結びついており、arr[i]*(arr + i) の配列構文です。ポインタ変数だけを使うことで、配列の仕組みがより実感できるはずです。

今回のサンプルコード(ポインタ演算版)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 学生レコードの構造体
typedef struct {
    int id;
    char name[50];
    int age;
} Student;

int main(void) {
    int count = 0;
    Student *students = NULL;   // 動的配列
    Student *p;                 // 作業用ポインタ

    // 1人目を追加
    count++;
    students = realloc(students, count * sizeof(Student));
    p = students + (count - 1);   // 最後の要素を指す
    p->id = 1;
    strcpy(p->name, "Taro");
    p->age = 18;

    // 2人目を追加
    count++;
    students = realloc(students, count * sizeof(Student));
    p = students + (count - 1);   // 最後の要素を指す
    p->id = 2;
    strcpy(p->name, "Hanako");
    p->age = 19;

    // 出力(ポインタを動かしながら)
    for (p = students; p < students + count; p++) {
        printf("%d: %s (%d)\n", p->id, p->name, p->age);
    }

    free(students);
    return 0;
}

実行結果(例)

前項のプログラムと同じです。

1: Taro (18)
2: Hanako (19)

ポインタ演算版のポイント解説

  • students + (count - 1)
    → 配列の最後の要素をポインタで表している
    (例: students + 1 は &students[1] と同じ意味)
  • p->id
    → (*p).id の省略形。ポインタで構造体メンバにアクセスする標準的な書き方。
  • ループの書き方
    for (p = students; p < students + count; p++)
    とすれば、インデックスを使わずにポインタを進めながら要素を処理できる。

ここでのまとめ

配列インデックスを使わずに ポインタ演算+->演算子 でレコードを操作できる
arr[i] と *(arr + i) が等価であることを実感できる

初学者には配列インデックスの方が分かりやすいが、ポインタ版を体験することで「C言語らしい書き方」や配列とポインタの関係が理解しやすくなる

ここでの補足

今回のコードはシンプルさを優先して realloc を直接使いましたが、実務では エラーチェック(realloc が失敗した場合の処理)を入れることが推奨されます。
学習の第一歩としては「ポインタで配列を歩く」ことを体験するのが目的です。

配列とポインタの関係について

配列とポインタはよく「同じものだ」と説明されることがあります。
しかし、正しくは 「密接に関係しているが別物」 です。

配列

  • 宣言時にサイズが決まり、変更できない
  • 実体は「決まった長さの連続したメモリ領域」
  • 配列名は式の中で 先頭要素のアドレスに暗黙変換 される

ポインタ

  • 「アドレスを保持する変数」
  • 代入によって、どのメモリを指すかを自由に変えられる
  • malloc / free と組み合わせると動的にメモリを扱える

似ている理由

  • arr[i]*(arr + i) に展開される
  • p[i]*(p + i) に展開される
  • そのため「同じように使える」と説明されがち

配列とポインタのまとめ

  • 配列は領域そのもの
  • ポインタはアドレスを保持する変数
  • 使い方が似ているため混同されやすいが、概念としては異なる

全体的なまとめ:配列から始まるデータ構造の考え方

今回の内容では、配列を使ってアドレス操作・ポインタ操作の基本を体験しました。
ここから先は、より複雑なデータ構造の理解につながっていきます。

  • 🔹 配列はデータ構造の入り口
    配列では「要素が連続したメモリ領域に並ぶ」という基本を学びました。
    これは、すべてのデータ構造の出発点です。

  • 🔹 アドレスを管理すれば自由に構造を作れる
    各データをアドレスでつなげれば、配列以外の構造も作れます。
    たとえば:

    • リスト(順番に次を指す)
    • ツリー(親・子の関係をポインタで表す)
    • スタック/キュー(追加・取り出しを制御)
  • 🔹 高水準言語ではライブラリが支えてくれる
    C++、C#、Java、Pythonなどの言語には、これらのデータ構造を扱うための標準ライブラリが用意されています。
    開発者はそれを使うだけで、内部のポインタ処理を意識せずに済みます。

  • 🔹 でもポインタを知っていると強い
    メモリ上で何が起きているかを理解できると、
    ・効率的なデータ処理
    ・バグの特定や最適化
    ・低レイヤとの連携(組込み、画像処理など)
    などで強みを発揮できます。

さいごに

今回の配列とポインタの操作は、データ構造を理解する第一歩です。配列を通して「アドレスをどう扱うか」が分かると、その先の世界(リスト、ツリー、メモリ管理)が一気に開けます。そこが見えてくる一歩になればと思います。

ありがとうございます。

(追記)今回で一段落させようと思ったのですが、もう一段階進めたほうがいいかなと思ったので、延長編として以下を投稿しました。
クラウド開発環境PaizaCloudクラウドIDEでHello World(C言語ポインタ 発展編)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?