C言語の構造体とポインタの基本を改めて考える

アイキャッチ画像

私はこれまで主にWeb制作やWebアプリケーション開発の現場で仕事をしてきました。HTMLやjQuery、WordPressのような比較的レガシーな領域から、Reactなどを使ったモダンなフロントエンド開発に携わり、バックエンドはFlaskなどで自己学習中という立場です。そんな私が最近、C言語を改めて学び直し、特に「構造体」と「ポインタ」の基本に向き合う機会がありました。

この記事では、C言語における構造体変数の扱いやポインタの意味、そして「値渡し」と「参照渡し(ポインタ渡し)」の違いを、Web系の開発経験者にもわかりやすく解説しながら紹介したいと思います。

構造体とは何か?クラスとどう違うのか?

C言語の構造体(struct)は、複数の異なる型のデータをひとまとめにできるデータ構造です。たとえば、学生の情報をまとめた構造体は以下のように定義します。

struct Student {

    int year;

    char name[64];

    double weight;

    double height;

};

ここでのStudentは、year(学年)、name(名前)、weight(体重)、height(身長)というメンバを持っています。

この構造体ですが、他の言語における「クラス」と似ているように見えますが、明確な違いがあります。

・メソッド(関数)を持てない

Cの構造体はデータの集まりであり、関数を内包することはできません。

・インスタンス化という概念が曖昧

C言語ではstructの変数を宣言することで実体を作りますが、オブジェクト指向の言語のような「newによる生成」や「コンストラクタ」はありません。

構造体はあくまでデータのまとまりであり、そこに動作を付与するのは別の関数として定義します。

ポインタとは何か?アドレスを指すだけのもの?

ポインタとは、変数の「アドレス(メモリ上の場所)」を指す変数です。たとえば、Student型の変数sがあったとして、Student *pはsのメモリアドレスを保持し、そこからデータを操作できるようにします。

・変数sは実際のデータの実体

・ポインタpはその実体の場所を示す

この「アドレスを扱う」という仕組みは、C言語の根幹の一つです。

構造体の変数とポインタ、両方を使う理由

では、構造体変数自体にポインタは存在するのでしょうか。

はい、存在します。通常の変数と同様に、構造体変数もメモリ上に実体として配置されるため、そのアドレス(ポインタ)を取得することが可能です。たとえば &student のように、変数名の前にアドレス演算子 & をつけることで、構造体変数のアドレスを取得できます。

なお、構造体変数自体は、あくまでデータの実体として振る舞います。その中身に直接アクセスしたい場合は、ドット演算子(.)を用いて各メンバーにアクセスします。

Student s;

s.year = 2;

大きな構造体を関数に渡したり、複数の場所で同じデータを共有したい場合はポインタを使います。

Student *p = &s; // sのアドレスをpに渡す

p->year = 3;     // ポインタ経由でsのyearを変更

ポインタを使うことで、コピーをせずに直接データを操作できるため、メモリや処理時間の節約になります。

値渡し(コピー)と参照渡し(ポインタ渡し)の違い

C言語で関数に構造体を渡すとき、値渡しポインタ渡しかを意識することが非常に重要であると感じました。

void update(Student s) {

    s.year = 3; // コピーなので外側には影響なし

}

void updatePointer(Student *s) {

    s->year = 3; // 実体のデータを変更する

}

値渡しの場合、関数の中で受け取ったsは呼び出し元の構造体の「コピー」です。つまり関数内で値を変えても呼び出し元のデータは変わりません。

一方、ポインタ渡しは、実体のアドレスを渡しているので、関数内での変更が呼び出し元に反映されます。

なぜC言語はポインタだらけなのか?

Web系でJavaScriptやPythonを扱う方は、オブジェクトはデフォルトで参照渡しされる感覚がありますよね。ところが、C言語は基本的に「コピー渡し」がデフォルトです。

そのため、大きな構造体を何度もコピーすると効率が悪くなるため、ポインタ渡しを多用します。

つまり、「Cのコードはポインタだらけになる」のは効率的なメモリ使用とパフォーマンスを考えた設計思想に基づいている、と言えそうです。

ポインタと構造体の理解がもたらすもの

構造体とポインタの正しい理解は、C言語を扱う上で避けて通れません。これらを制することで、効率的かつ安全なコードが書けるようになると感じています。

私自身、学生時代にC言語のポインタや構造体を学んだときは正直なところ「なんでこんなややこしいの?」と思っていました。しかし、C言語が活躍する組み込み系の現場について理解を深めたり、別の言語との比較を進めたりすることでC言語の変数宣言やポインタ、構造体といった概念についての理解が深まり、それらが「自然」であると感じられるようになりました。

まとめ

・C言語の構造体はクラスとは違い、データの集合体でメソッドは持てない

・ポインタは「アドレスを指す変数」で、構造体のコピーを避けて効率化するために使う

・関数に構造体を渡すときは、値渡し(コピー)かポインタ渡し(参照渡し)かで挙動が大きく変わる

・Web系の言語とは異なるCのメモリ管理・処理設計を理解することが、組み込み系や低レイヤー開発の第一歩になる

Web系で使う言語と異なり、C言語のポインタと構造体はまさに「原始的だけど強力な武器」です。これをしっかり理解しておくことで、より深いレベルでのプログラミング技術が身に付くのかなと感じました。