構造体変数は、それ自体が1つの変数として扱われます。
従って、構造体型の引数を使うことが出来、1度に複数の情報を渡すことが出来ます。
構造体型の引数も、今までの引数と全く同じ方法で指定出来ます。
ただし、typedefで宣言していない場合は、structをタグ名の前につける必要があります。
次の関数は、student型の構造体変数を引数として受け取る関数です。
受け取る側の関数での使い方も、通常の引数と全く同じです。void student_print(student data)
配列の時は、引数にしても、渡されるのは先頭アドレスだけでしたが、void student_print(student data) { printf("[学年]:%d\n",data.year); printf("[クラス]:%d\n",data.class); 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 class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ } student; void student_print(student data); int main(void) { student data; data.year = 3; data.class = 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.class); printf("[出席番号]:%d\n",data.number); printf("[名前]:%s\n",data.name); printf("[身長]:%f\n",data.stature); printf("[体重]:%f\n",data.weight); return; }
この関数は、student型の構造体変数の中身を全て表示します。
[学年]:3
[クラス]:4
[出席番号]:18
[名前]:MARIO
[身長]:168.200000
[体重]:72.400000
[ 構造体の中の配列 ]
構造体の中に配列が含まれている場合は、配列の中身もコピーされて渡されます。
従って、中身を変更しても、呼び出し元の変数には影響しません。
構造体型のポインタ変数を作ることが出来ます。宣言の方法などは同じです。
次のプログラムは、student型へのポインタ変数を使用する例です。
構造体の各要素は、宣言の時の順番通りに並んでおり、#include <stdio.h> typedef struct { int year; /* 学年 */ int class; /* クラス */ 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 class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ } student; void student_print(student *data); int main(void) { student data; data.year = 3; data.class = 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->class); printf("[出席番号]:%d\n",data->number); printf("[名前]:%s\n",data->name); printf("[身長]:%f\n",data->stature); printf("[体重]:%f\n",data->weight); return; }
普通に渡すことが出来る構造体を、ポインタ変数として渡す理由の
1つ目は、普通のポインタ変数と同じく、関数内で値を変更出来るようにするためです。
ここでは試していませんが、関数内で値を変えると呼び出し元の変数の中身も変わります。
2つ目は、関数呼び出しの高速化のためです。
構造体を渡す時、その中身は全てコピーされます。
構造体の中に、大きな配列があれば、その中身までまるごとコピーされます。
これは、当然ながらそれなりに時間のかかる処理となります。
しかし、ポインタのアドレス値を渡すだけなら、ほとんど時間はかかりません。
一般には、構造体はポインタ変数を使って受け渡しすることが多いようですが、
中の値を書き換えてしまうなどのトラブルにも見舞われやすいので、
慣れない内は、普通に受け渡ししていた方が簡単だと思います。