MMGamesロゴ  MMGames
Twitterシェアボタン  Facebookシェアボタン   
しんで覚えるC言語
しんで覚えるC言語

構造体の引数

構造体で情報を渡す
構造体変数は、それ自体が1つの変数として扱われます。
したがって、構造体型の引数を使うことが出来、1度に複数の情報を渡すことができます。

構造体型の引数も、今までの引数とまったく同じ方法で指定できます。
ただし、typedefで宣言していない場合は、structをタグ名の前につける必要があります。
次の関数は、student型の構造体変数を引数として受け取る関数です。

student型の構造体変数を引数として受け取る関数
void student_print(student data)

受け取る側の関数での使い方も、通常の引数とまったく同じです。
次の関数は、student型の内容をすべて表示するものです。

student型の内容をすべて表示する関数
void student_print(student data)
{
    printf("[学年]:%d\n", data.year);
    printf("[クラス]:%d\n", data.clas);
    printf("[出席番号]:%d\n", data.number);
    printf("[名前]:%s\n", data.name);
    printf("[身長]:%f\n", data.stature);
    printf("[体重]:%f\n", data.weight);
    return;
}

配列の時は、引数にしても、渡されるのは先頭アドレスだけでしたが、
構造体型の引数は、受け取る側の関数にすべての値がコピーされます
したがって、受け取る側の関数で引数の中身を変更しても、元の構造体変数には影響しません。

呼び出し側でも、通常の変数とまったく同じ方法で呼び出すことができます。
次のプログラムは、先ほどの関数を呼び出す例です。

ソースコード
#include <stdio.h>
#include <string.h>

typedef struct
{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
} student;

void student_print(student data);

int main(void)
{
    student data;

    data.year = 3;
    data.clas = 4;
    data.number = 18;
    strcpy(data.name, "MARIO");
    data.stature = 168.2;
    data.weight = 72.4;

    student_print(data); /* 呼び出し */

    return 0;
}

void student_print(student data)
{
    printf("[学年]:%d\n", data.year);
    printf("[クラス]:%d\n", data.clas);
    printf("[出席番号]:%d\n", data.number);
    printf("[名前]:%s\n", data.name);
    printf("[身長]:%f\n", data.stature);
    printf("[体重]:%f\n", data.weight);
    return;
}

このプログラムの実行結果は次の通りになります。

実行結果
[学年]:3
[クラス]:4
[出席番号]:18
[名前]:MARIO
[身長]:168.200000
[体重]:72.400000

この関数は、student型の構造体変数の中身をすべて表示します。

構造体の中の配列
構造体の中に配列が含まれている場合は、配列の中身もコピーされて渡されます。
したがって、中身を変更しても、呼び出し元の変数には影響しません。
配列をコピーして渡したい時(たとえば、リバーシのプログラムで盤面情報を渡したい等)には、
構造体にしてしまうのが一番簡単です。

構造体でもポインタ変数
構造体型のポインタ変数を作ることができます。宣言の方法などは同じです。
次のプログラムは、student型へのポインタ変数を使用する例です。

ソースコード
#include <stdio.h>
#include <string.h>

typedef struct
{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
} student;

int main(void)
{
    student data;
    student *pdata;

    pdata = &data;                  /* 初期化 */
    (*pdata).year = 10;             /* 通常変数モードへの切り替え */
    strcpy((*pdata).name, "MARIO"); /* 通常変数モードへの切り替え */

    return 0;
}

構造体の各要素は、宣言の時の順番通りに並んでおり、
&演算子で求められるアドレスは、構造体の始めの要素のアドレスです。

構造体のポインタ変数の場合も、*記号で通常変数モードに切り替えることができます。
ただし、.の方が優先されるので、かっこをつけて次のようにします。

構造体のポインタ変数経由で要素にアクセス
(*構造体ポインタ変数名).要素名

ただ、(*)をつけるのは面倒なので、次の書き方で代用できるようになっています。

構造体のポインタ変数経由で要素にアクセス
構造体ポインタ変数名->要素名

->は、引き算と比較記号を組み合わせた記号です。
例によって、引き算とも比較ともまったく関係はなく、記号を流用しているだけです
この書き方なら、より簡易的な書式で、各要素にアクセスできます。
次のプログラムは、->記号を使って各要素にアクセスする例です。

ソースコード
int main(void)
{
    student data;
    student *pdata;

    pdata = &data;                /* 初期化 */
    pdata->year = 10;             /* ->記号によるアクセス */
    strcpy(pdata->name, "MARIO"); /* ->記号によるアクセス */

    return 0;
}

構造体でもポインタ引数
前項では、構造体でもポインタ変数にすることができると説明しましたが、
同様にして、構造体型へのポインタ型の引数を持つ関数も作ることができます。
次のプログラムは、先ほどの関数を、ポインタ変数を使うよう改めた例です。

ソースコード
#include <stdio.h>
#include <string.h>

typedef struct
{
    int year;       /* 学年 */
    int clas;       /* クラス */
    int number;     /* 出席番号 */
    char name[64];  /* 名前 */
    double stature; /* 身長 */
    double weight;  /* 体重 */
} student;

void student_print(student *data);

int main(void)
{
    student data;

    data.year = 3;
    data.clas = 4;
    data.number = 18;
    strcpy(data.name, "MARIO");
    data.stature = 168.2;
    data.weight = 72.4;

    student_print(&data); /* アドレスで呼び出す */

    return 0;
}

void student_print(student *data)
{
    printf("[学年]:%d\n", data->year); /* ->記号でアクセス */
    printf("[クラス]:%d\n", data->clas);
    printf("[出席番号]:%d\n", data->number);
    printf("[名前]:%s\n", data->name);
    printf("[身長]:%f\n", data->stature);
    printf("[体重]:%f\n", data->weight);
    return;
}

まず、引数の型がポインタ型として宣言されていることがわかります。
関数を呼び出す時には、&演算子をつけて、アドレスを渡していることもわかります。
また、呼び出された関数内では、->記号で各要素にアクセスしています。

普通に渡すことができる構造体を、ポインタ変数として渡す理由の
1つ目は、普通のポインタ変数と同じく、関数内で値を変更できるようにするためです。
ここでは試していませんが、関数内で値を変えると呼び出し元の変数の中身も変わります。

2つ目は、関数呼び出しの高速化のためです。
構造体を渡す時、その中身はすべてコピーされます。
構造体の中に、大きな配列があれば、その中身までまるごとコピーされます。
これは、当然ながらそれなりに時間のかかる処理となります。
しかし、ポインタのアドレス値を渡すだけなら、非常に高速です。

しかし、現代のコンピュータはとても高速なので、値渡しであっても、それほど問題にはなりません。
慣れないうちは、値渡しのほうが扱いやすいため、値渡しで行うことをおすすめします。


本サイトについて

苦しんで覚えるC言語(苦C)は
C言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。

第0部:プログラム概要編
  1. プログラムとは何か?
2章:プログラムの書き方
  1. 書き方のルール
  2. 書き方の慣習
  3. 練習問題2
3章:画面への表示
  1. 文字列の表示
  2. 改行文字
  3. 練習問題3
6章:キーボードからの入力
  1. 入力用の関数
  2. 入力の恐怖
  3. 練習問題6
9章:回数が決まっている繰り返し
  1. 繰り返しを行う文
  2. ループ動作の仕組み
  3. 練習問題9
10章:回数がわからない繰り返し
  1. 回数不明ループ
  2. 入力チェック
  3. 練習問題10
13章:複数の変数を一括して扱う
  1. 複数の変数をまとめて扱う
  2. 配列の使い方
  3. 練習問題13
20章:複数のソースファイル
  1. 最小限の分割
  2. 分割の定石
  3. 練習問題20

コメント
COMMENT

💬 コメント投稿欄を開く