C言語で構造体を引数に渡す方法

383, 2022-01-05

目次

動画

C言語の構造体を引数に渡す方法

structure

C言語では構造体を扱うことができます。
この記事では構造体を関数の引数に渡す方法を具体的に解説します。

構造体は関数とともによく使われます。
そのため関数における構造体の扱い方を押さえておくのは有用と言えます。
今回は↓を中心に解説します。

  • 構造体を引数にコピーして渡す

  • 構造体のポインタを引数に渡す

  • 構造体の配列を引数に渡す

関連記事
C言語で構造体を代入する方法
C言語で構造体を比較する【memcmpの危険性】
C言語で構造体を引数に渡す方法
C言語の構造体をコピーする
C言語の構造体を関数で扱う

構造体を引数にコピーして渡す

pass

↓のような関数の定義があったとき、この関数の引数に構造体を渡すと構造体がコピーされます。

void func(struct Animal arg) {
    ...
}

具体的には↓のようなコードになります。

#include <stdio.h>

// 動物を表す構造体
struct Animal {
    int age;  // 年齢
    char name[40];  // 名前
};

// 引数のargには構造体がコピーされる
void func(struct Animal arg) {
    // argのメンバを出力
    printf("age[%d] name[%s]\n", arg.age, arg.name);
}

int main(void) {
    // cat変数を定義する
    struct Animal cat = { 20, "Tama" };

    // funcにcatをコピーして渡す(値渡し)
    func(cat);

    return 0;
}

上記のコードをコンパイルして実行すると↓のような結果になります。

age[20] name[Tama]

上記のコードではmain関数内でstruct Animal構造体の変数catを定義しています。
そしてそのcatfunc()に渡しています。

    // cat変数を定義する
    struct Animal cat = { 20, "Tama" };

    // funcにcatをコピーして渡す(値渡し)
    func(cat);

この時、関数の呼び出し側の引数を実引数、関数の定義側の引数を仮引数と言います。
実引数catfunc()の仮引数argにコピーされて渡されます。

// 引数のargには構造体がコピーされる
void func(struct Animal arg) {
    // argのメンバを出力
    printf("age[%d] name[%s]\n", arg.age, arg.name);
}

引数argにはcatのデータがコピーされていますので、catargはそれぞれ別のメモリ上に確保されています。
そのためmain()関数側でcatのデータを変更したり、func()側でargのデータを変更しても、catargそれぞれは干渉しません。

このような値渡しによる引数への構造体の渡し方は、一般的にあまり効率がよくありません
というのも、構造体のメンバが増えればそれだけコピーのコストが増えるからです。
そのため、速度を必要とするプログラムでは後述するポインタ渡し(アドレス渡し)のほうが一般的です。

構造体のポインタを引数に渡す

pointer

構造体のポインタを関数の引数に渡すには↓のようにします。

#include <stdio.h>

// 動物を表す構造体
struct Animal {
    int age;  // 年齢
    char name[40];  // 名前
};

// 引数のargには構造体のポインタが渡される
void func(struct Animal *arg) {
    // argのメンバを出力
    printf("age[%d] name[%s]\n", arg->age, arg->name);
}

int main(void) {
    // cat変数を定義する
    struct Animal cat = { 20, "Tama" };

    // funcにcatのアドレス(ポインタ)を渡す
    func(&cat);

    return 0;
}

func()関数の引数は「struct Animal *arg」です。
これはstruct Animalのポインタ変数(ポインタ引数)になります。

// 引数のargには構造体のポインタが渡される
void func(struct Animal *arg) {
    // argのメンバを出力
    printf("age[%d] name[%s]\n", arg->age, arg->name);
}

main()関数側では構造体変数のcatのアドレス(ポインタ)をfunc()に渡しています。

    // cat変数を定義する
    struct Animal cat = { 20, "Tama" };

    // funcにcatのアドレス(ポインタ)を渡す
    func(&cat);

変数の頭に&(アンパサンド)を付けるとその変数のアドレスを取り出すことができます。

このような「struct Animal *arg」という引数は関数内で構造体のデータを間接的に参照したい&変更したい場合などによく使われます。
constを付けていないのでargで参照できるデータはfunc()内でも変更が可能です。

void func(struct Animal *arg) {
    arg->age = 30;
    strcpy(arg->name, "Hige");
}

参照だけしてデータを変更する予定がない場合は引数にconstを付けて「const struct Animal *arg」のように書きます。

// constを付けたポインタ
void func(const struct Animal *arg) {
    // 書き込みできないのでエラーになる
    arg->age = 30;
}

構造体の配列を引数に渡す

array

構造体の配列も関数の引数に渡せます。

#include <stdio.h>

// 動物を表す構造体
struct Animal {
    int age;  // 年齢
    char name[40];  // 名前
};

// 配列を引数に取る
void func(struct Animal animals[]) {
    // animalsを出力
    printf("0: age[%d] name[%s]\n", animals[0].age, animals[0].name);
    printf("1: age[%d] name[%s]\n", animals[1].age, animals[1].name);
    printf("2: age[%d] name[%s]\n", animals[2].age, animals[2].name);
}

int main(void) {
    // 配列を定義する 
    struct Animal cats[] = {
        { 20, "Tama" },
        { 30, "Pochi" },
        { 40, "Yamada" },
    };

    // funcに配列を渡す
    func(cats);

    return 0;
}

func()関数の引数は「struct Animal animals[]」になってます。
これは一見すると配列のように見えますが、実体はただのポインタです。
ですので配列を引数に渡す場合は関数側の引数を「struct Animal *animals」にしても大丈夫です。
見た目的には[]にしたほうが配列だとわかるので、配列の場合は*ではなくて[]が使われることが多くあります。

おわりに

今回はC言語の構造体を関数の引数に渡す方法を解説しました。
構造体は関数とセットでよく使われます。

構造体を引数にシュート

C言語マスターに俺はなる