
C言語基礎|構造体を返す関数
「関数は値を1つしか返せない?…いいえ、構造体なら“ひとまとめ”で返せます!」
C言語の関数って、基本は戻り値が1つですよね。
でも現実の処理では「値を3つまとめて返したい!」みたいな場面、けっこうあります。
たとえば、計算結果として「合計・平均・最大」をまとめて返したい、みたいなケース。
ここで構造体が活躍します。構造体は 代入できる ので、関数の戻り値として 構造体そのもの を返せるんです。
つまり、関数の戻り値を構造体にすると
複数の値をセットにして返せる → コードが素直で読みやすくなる、というわけです。

構造体を返せる理由(配列との対比)
ポイントはここです:構造体は代入できる。
だから「返して、受け取って、代入する」が自然にできます。
配列と構造体の違い(戻り値にできる?)
| 型 | 代入 | 関数の戻り値 | ひとこと |
|---|---|---|---|
| 配列 | できない | そのままはできない | 配列そのものは代入できない |
| 構造体 | できる | できる | 丸ごとコピー(代入)できる |
まずは全体像:構造体を返す流れ
構造体を返して受け取る
(1) 関数の中で構造体 temp を作る
(2) temp に値を詰める
(3) return temp; で丸ごと返す
(4) 呼び出し側で result = func(...); で受け取る
説明
- return で返ってくるのは「構造体1つ」だけど
- その中にメンバが複数あるので
- 実質「複数の値をまとめて返している」状態になります
サンプルプログラム
買い物計算の結果(合計・税込・おつり) を返すプログラム例です。
仕様
- 単価 price と個数 count と支払い cash を受け取る
- 合計 total、税込 tax_included、おつり change をまとめて返す
- 返ってきた構造体を表示する
プロジェクト名:chap12-6-1 ソースファイル名:chap12-6-1.c
#include <stdio.h>
// 計算結果をまとめる構造体
struct Bill {
int total; // 合計(税抜)
int taxIncluded; // 税込(10%として計算)
int change; // おつり
};
// 合計・税込・おつりをまとめて返す
struct Bill make_bill(int price, int count, int cash)
{
struct Bill temp;
temp.total = price * count;
temp.taxIncluded = temp.total + (temp.total / 10); // 10%の簡易計算
temp.change = cash - temp.taxIncluded;
return temp; // 構造体を丸ごと返す
}
int main(void)
{
struct Bill b;
b = make_bill(120, 3, 500);
printf("計算結果です。\n");
printf("合計(税抜):%d 円\n", b.total);
printf("合計(税込):%d 円\n", b.taxIncluded);
printf("おつり:%d 円\n", b.change);
return 0;
}登場する命令(要素)の書式と役割を丁寧に解説
構造体の宣言
書式
struct タグ名 {
型 メンバ名;
...
};
何をする?
- 「複数の値をまとめて扱う型」を作ります。
- Bill の例では、合計・税込・おつりをひとまとめにしています。
構造体を返す関数
書式
struct 型名 関数名(引数...)
{
struct 型名 変数;
... 値をセット ...
return 変数;
}
何をする?
- 関数の中で構造体を作って値を詰め、構造体の値を丸ごと返します。
- 呼び出し側は、受け取った構造体をそのまま変数に代入できます。
return
書式
return 式;
何をする?
- 関数の実行を終えて、呼び出し元へ値を返します。
- 今回は return temp; により temp(構造体)全体が返ります。
関数呼び出し式の評価と代入
書式
変数 = 関数名(引数...);
何をする?
- 関数名(引数...) という式を評価すると「戻り値」が得られます。
- その戻り値(構造体)が、左辺の構造体変数へ丸ごと代入されます。
図:返ってくる構造体のイメージ
例:make_bill(120, 3, 500) の結果

図の説明
- 関数は「構造体1つ」を返す。
- でも構造体の中にはメンバが3つある。
- だから、まとめて持ち帰れる、という感覚になります。
余談:名前空間(同じ名前が使える理由)
C言語には「名前空間」が複数あり、種類が違えば同じ綴りを使っても衝突しません。
名前空間の4種類
| 種類 | 例 | どこで使われる? |
|---|---|---|
| ラベル名 | start: | goto の飛び先 |
| タグ名 | struct X | struct/union/enum のタグ |
| メンバ名 | obj.x | 構造体の中の名前 |
| 一般的な識別子 | 変数名、関数名 | ふだんの名前 |
同じ x でも種類が違う(イメージ)
struct x { // タグ名
int x; // メンバ名
} x; // 変数名
x: // ラベル名
x.x = 1; // 変数名.メンバ名
説明
- タグ名の x、メンバ名の x、変数名の x、ラベル名の x は
- 「所属する名前空間」が違うので同居できます
演習問題
演習12-2(数値3つを読み込んで構造体で返す)
int 型の price と count と cash をキーボードから読み込み、
make_bill と同じ計算結果を持つ struct Bill を返す関数を作ってください。
関数の形:
struct Bill scan_bill(void);
解答例
プロジェクト名:chap12-6-2 ソースファイル名:chap12-6-2.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
struct Bill {
int total;
int taxIncluded;
int change;
};
struct Bill scan_bill(void)
{
int price, count, cash;
struct Bill temp;
printf("単価を入力:");
scanf("%d", &price);
printf("個数を入力:");
scanf("%d", &count);
printf("支払い金額を入力:");
scanf("%d", &cash);
temp.total = price * count;
temp.taxIncluded = temp.total + (temp.total / 10);
temp.change = cash - temp.taxIncluded;
return temp;
}
int main(void)
{
struct Bill b = scan_bill();
printf("入力から計算しました。\n");
printf("合計(税抜):%d 円\n", b.total);
printf("合計(税込):%d 円\n", b.taxIncluded);
printf("おつり:%d 円\n", b.change);
return 0;
}解説(ポイント)
- scan_bill の戻り値は struct Bill
- 関数内で temp を作り、値を詰めて return temp
- 呼び出し側は struct Bill b = scan_bill(); のように受け取れる
演習12-3(名前と数値を読み込んで構造体で返す)
学生ではなく、ユーザー設定を返す例に置き換えます。
名前(文字列)とレベル(int)を読み込んで、設定情報をまとめた構造体を返す関数を作ってください。
関数の形:
struct Profile scan_profile(void);
解答例
プロジェクト名:chap12-6-3 ソースファイル名:chap12-6-3.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
#define NAME_LEN 32
struct Profile {
char name[NAME_LEN];
int level;
};
struct Profile scan_profile(void)
{
struct Profile temp;
printf("表示名を入力:");
scanf("%s", temp.name);
printf("レベルを入力:");
scanf("%d", &temp.level);
return temp;
}
int main(void)
{
struct Profile p = scan_profile();
printf("登録しました。\n");
printf("名前:%s\n", p.name);
printf("レベル:%d\n", p.level);
return 0;
}
解説(ポイント)
- name は配列なので scanf で & は不要
- level は int なので scanf では & が必要
- まとめて返すので、呼び出し側がスッキリします。
