Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

【C言語】構造体へのポインタと動的メモリ割り当て(malloc)の使い方を解説

構造体へのポインタとは?

構造体へのポインタとは、構造体全体の先頭アドレスを保持するポインタのことです。

連結リスト、木構造(ツリー)、グラフといった複雑なデータ構造を実装する際に不可欠な概念であり、C言語プログラミングの中核を担う重要な仕組みの一つです。

また、ポインタから構造体のメンバにアクセスする際には、アロー演算子(->)と呼ばれる特殊な演算子を使用します。

宣言の方法

C言語で構造体へのポインタを宣言する構文は以下の通りです。

struct tagname *ptr;

記述例:

struct student *s;

メンバへのアクセス

ポインタが指す構造体のメンバには、次の形式でアクセスします。

Ptr->membername;

記述例:s->sno、s->sname、s->marks など。

例1:malloc関数による動的メモリ割り当て

以下は、malloc関数を使って構造体のメモリを実行時に動的に確保するC言語のサンプルプログラムです。ユーザーが入力した人数分のstruct person型メモリを一括で確保し、ポインタ演算((ptr+i))によって各要素へアクセスしています。

#include <stdio.h>
#include <stdlib.h>
struct person {
    int age;
    float weight;
    char name[30];
};
int main(){
    struct person *ptr;
    int i, n;
    printf("Enter the number of persons: ");
    scanf("%d", &n);
    /* n人分のstruct person用のメモリを確保 */
    ptr = (struct person*) malloc(n * sizeof(struct person));
    for(i = 0; i < n; ++i){
        printf("Enter name and age respectively: ");
        /* 1人目のメンバには ptr->name、ptr->age でアクセスし、
           2人目以降は (ptr+1)->name、(ptr+1)->age を使用 */
        scanf("%s %d", (ptr+i)->name, &(ptr+i)->age);
    }
    printf("Displaying Information:\n");
    for(i = 0; i < n; ++i)
        printf("Name: %s\tAge: %d\n", (ptr+i)->name, (ptr+i)->age);
    return 0;
}

実行結果

上記のプログラムを実行すると、次のような出力が得られます。

Enter the number of persons: 1
Enter name and age respectively: bhanu 24
Displaying Information:
Name: bhanu Age: 24

ここでのポイントは、sizeof演算子で構造体1つ分のサイズを取得し、それに入力された人数 n を掛けたサイズだけ malloc で確保している点です。これにより、実行時までデータ件数が不明な場合でも柔軟に対応できます。なお、確保したメモリは使用後に free(ptr) で解放するのが安全な作法です。

例2:ポインタのインクリメントによる複数データの処理

続いて、構造体配列のアドレスをポインタに設定し、「p++」でポインタを1つずつ進めながら複数人のデータを入力・表示する例を紹介します。

#include<stdio.h>
/* 外側の構造体を宣言 */
struct Manager{
    char Name[15];
    int Age;
    char Gender;
    float Level;
    char Role[50];
    char temp;
}m[20];
void main(){
    /* ループ用変数とポインタ変数を宣言 */
    int i;
    struct Manager *p;
    /* ポインタに配列の先頭アドレスを設定 */
    p=&m;
    /* ユーザー入力の読み込み */
    for (i=1;i<3;i++){ /* 2人分のマネージャーデータを受け付ける */
        printf("Enter the Name of manager %d : ",i);
        gets(p->Name);
        printf("Enter the Age of manager %d : ",i);
        scanf("%d",&p->Age);
        scanf("%c",&p->temp);/* バッファのクリア */
        printf("Enter the Gender of manager %d : ",i);
        scanf("%c",&p->Gender);
        printf("Enter the level of manager %d : ",i);
        scanf("%f",&p->Level);
        scanf("%c",&p->temp);/* バッファのクリア */
        printf("Enter the role of manager %d : ",i);
        gets(p->Role);
        p++;
    }
    /* 出力用にポインタを先頭へ戻す */
    p=&m;
    /* 結果の出力 */
    for (i=1;i<3;i++){
        printf("The Name of Manager %d is : %s\n",i,p->Name);
        printf("The Age of Manager %d is : %d\n",i,p->Age);
        printf("The Gender of Manager %d is : %c\n",i,p->Gender);
        printf("The Level of Manager %d is : %f\n",i,p->Level);
        printf("The Role of Manager %d is : %s\n",i,p->Role);
        p++;
    }
}

実行結果

上記のプログラムを実行すると、次のような出力が得られます。

Enter the Name of manager 1 : Hari
Enter the Age of manager 1 : 55
Enter the Gender of manager 1 : M
Enter the level of manager 1 : 2
Enter the role of manager 1 : Senior
Enter the Name of manager 2 : Bob
Enter the Age of manager 2 : 60
Enter the Gender of manager 2 : M
Enter the level of manager 2 : 1
Enter the role of manager 2 : CEO
The Name of Manager 1 is : Hari
The Age of Manager 1 is : 55
The Gender of Manager 1 is : M
The Level of Manager 1 is : 2.000000
The Role of Manager 1 is : Senior
The Name of Manager 2 is : Bob
The Age of Manager 2 is : 60
The Gender of Manager 2 is : M
The Level of Manager 2 is : 1.000000
The Role of Manager 2 is : CEO

このプログラムでは、scanf の直後にダミー変数 temp へ読み込ませてバッファ内の改行文字を取り除くことで、性別(%c)やレベル(%f)が正しく入力されるよう工夫されています。

なお補足ですが、gets 関数はバッファオーバーフローの危険性があるため、近年のコンパイラでは非推奨(警告対象)となっています。実際の開発では fgets の使用が推奨されます。

  1. C言語におけるユニオンとポインタの使い方を徹底解説

    ユニオン(共用体)とは、異なるデータ型を持つ複数の変数が、同一のメモリ領域を共有するための仕組みです。構造体が各メンバーごとに独立したメモリを割り当てるのに対し、ユニオンではすべてのメンバーが同じアドレスを参照するという点が大きな特徴です。 ユニオンの構文 ユニオンを定義するときの基本的な書式は次の通りです。 union タグ名{ データ型 メンバー1; データ型 メンバー2; ---- ---- データ型 メンバーn; }; 実際の記述例を見てみましょう。 union sample{ int a; float b; char

  2. C言語のポインタ入門!宣言・初期化から配列アクセスの実例まで徹底解説

    ポインタ(pointer)とは、他の変数のメモリアドレスを格納するための変数です。C言語においてポインタは、メモリを直接操作したり、関数間でデータを効率的にやり取りしたりするために欠かせない重要な概念です。 ポインタの宣言・初期化とアクセス まず、次のような通常の整数変数の宣言を見てみましょう。 int qty = 179; 変数qtyには値「179」が格納されると同時に、メモリ上のどこかにその格納場所(アドレス)が割り当てられます。 ポインタの宣言 int *p; 「p」はポインタ変数であり、別の整数型(int)変数のアドレスを保持します。変数名の前にアスタリスク(*)を付けることで、「こ