C言語 関数を詳細図解【理解するために必要な捉え方のイメージ】

C言語
この記事は約26分で読めます。

こんにちは、ナナです。

ここまででプログラムにおける基本要素である「順次」「分岐」「反復」を学び終わりました。ここで新たに「関数」と呼ばれる要素を追加しましょう。

本記事では次の疑問点を解消する内容となっています。

本記事で学習できること
  • 関数の考え方と役割とは?
  • 関数は魔法のランプってどういうこと?
  • 関数の作り方と使い方とは?
  • 関数名・引数・戻り値って何?役割は?
  • 関数定義時によくやるビルドエラーとは?
  • 関数と変数の関係性とは?
  • 関数が動かない?そんな時に使う3種類のステップ実行って何?

では、関数の使い方を学んでいきましょう。

  1. 関数とは処理のブロック
  2. 関数は願いを叶える魔法のランプ
    1. 関数に似た身の回りにあるサービスとは
    2. printfの正体は関数
    3. printf関数の使い方
  3. 関数の部品構成と使い方
    1. 引数は入力情報・戻り値は出力情報
    2. return文の役割は関数の処理を終了すること
    3. 戻り値の型とreturnの関係
    4. 引数の与え方と戻り値の受け取り方
    5. 関数の引数と戻り値の型で使えるvoid型
  4. 関数定義時によくやってしまう注意事項
    1. 関数定義の中で関数定義はできない
    2. 関数定義の順番に注意しよう
  5. 関数と変数の関係
    1. ローカル変数とグローバル変数
    2. 関数呼び出しによる変数のラベル剥がしのタイミング
  6. 関数をデバッグするための3つのステップ実行
    1. 関数の中に入るためのステップイン
    2. 関数を実行したことにするステップオーバー
    3. 関数の外に出るためのステップアウト
  7. Q&A:関数に関するよくある質問
    1. Q:今までプログラムを書いてきたmainって関数だったの?
    2. Q:変数は全部グローバル変数でいいのでは?ローカル変数とかいらないんじゃないの?
    3. Q:関数定義の引数において、省略された書き方はOKなのか?
    4. Q:関数名の付け方に悩みます。考え方のコツは?
    5. Q:関数定義の際に引数や戻り値はどんな情報にするべき?
    6. Q:関数を定義するときに気をつけるべきこととは?
  8. 課題:関数の作り方と使い方が学べたかを確認しよう
    1. 課題1
    2. 課題2
    3. 課題3
    4. 課題4
スポンサー

関数とは処理のブロック

あなた、わたくしが登場する記事を1回飛ばしましたわね。この記事できっちり、その借りを返していただくわっ。関数とやらについてお教えなさいっ!

ナナ
ナナ

(飛ばしちゃったことバレてるの?!汗)
この関数の記事は量が多いから、満を持して登場いただきましたよ!関数はプログラムを作る上で欠かせない要素だから何回も読んで理解してね。

プログラムにおける関数とは、いくつかの処理を一つにまとめたブロックのことを言います。

これから実際にたくさんのプログラムを作っていくと、何回も同じ処理を記述しなければならないケースが出てきます。

重複した処理

このような場面では「関数」を作ることにより、同じ処理を1つにまとめることができます。

関数に置き換え

ソフトウェア開発者は同じことを何度も記述することを嫌います。同じ処理があれば「関数を作って処理をまとめよう」と考えます。

プログラムの中で関数を作ることを「関数定義」、利用することを「関数呼び出し」といいます。

スポンサー

関数は願いを叶える魔法のランプ

関数があれば同じことをする必要がなくなるということね。その程度のものなの?大したことないじゃないの。とんだ拍子抜けだわ。

ナナ
ナナ

関数は重複処理を一つにまとめるという見方もあるけど、実際は「サービス」という姿として扱うことが多いね。こちらの方が関数というものの便利さを表現しているといってもいいね。

関数は「複数の処理をまとめたブロック」という見方以外に「サービスを提供するブロック」という見方ができます。

関数はプログラムを作るうえで非常に便利なものであり、「関数を呼べば願いが叶う」という魔法のランプのようなものです。

関数はサービス

関数を呼び出す側は、関数が動作するために必要とする情報を入力情報として与えます。呼び出された関数側は入力情報に応じた結果を出力します。それが関数というサービスです。

願いを叶える魔法のランプ?!わたくしにふさわしいものじゃないの。いつでも呼べば願いを聞いてくれるって、まるで「じいや」のようだわ。

執事
執事

お呼びでございますか、お嬢様。何なりとお申し付けを。

ナナ
ナナ

え!この人誰っ!じいや?いつからいたの?

関数に似た身の回りにあるサービスとは

関数を具体的にイメージできるように皆さんが生活する中で関数と似たものを示しましょう。

「自動販売機」がひとつの例です。 のどが渇いて飲み物が欲しいと思った皆さんは、自動販売機というサービスを利用することで飲み物を得ることができます。

関数の概念

関数にはそれぞれ提供するサービス内容があり、サービスに必要な入力情報は異なります。関数を呼び出す側は対象のサービスを動かすための情報を用意する必要があります。

ナナ
ナナ

関数はサービス」と捉えることがプログラムの関数を理解するコツです。

みんなの生活の周りには気づいていないだけでサービスが溢れています。「このサービスの入力と出力はなんだろう?」と日々考えると関数が身近に感じることでしょう。

printfの正体は関数

皆さんはここまでのプログラムでprintf命令を使ってきました。もうおわかりでしょう、printf命令の正体は関数です。

printf関数は文字列を入力情報として与えると、ディスプレイに文字列を表示してくれるサービスを提供しています。

printf関数

関数を呼び出す側は次のものを最低限知っておく必要があります。

関数を呼び出すために必要なもの
  • 関数名
  • その関数が必要とする入力情報
  • 関数から得られる出力情報

つまり、関数の中でどのような処理が行われているかまでは知らなくても使うことができます。

printf関数が入力情報として与えた文字をどんな仕組みでディスプレイに表示されるのか私は知りませんし、自動販売機がどんな仕組みで飲み物を出しているのか皆さん知りませんよね 。

関数に対し必要な入力情報を与えることができれば、関数の中の処理は知らなくても結果が得られるのが関数であり、便利な魔法のランプなのです 。

printf関数の使い方

ここまで漠然とprintf関数を使ってきましたが、printf関数の使い方を解説しておきましょう。

printf関数は画面上に皆さんが指定した文字や変数の中身を表示することができます。printf関数には様々な指定方法がありますが、例として次のように使われます。

// 指定した文字を表示
printf("出力書式");

// 書式に従い変数の値を1つ表示
printf("出力書式", 変数);

// 書式に従い変数の値を2つ表示
printf("出力書式", 変数, 変数);

“出力書式”の部分には画面に出力したい内容を記載します。

この中では変換指定子と呼ばれる特殊な記号を含ませることができます。代表的なものは次のものです。

printfの変換指定子

出力書式の中には特殊文字も含むことができ、代表的なものは次のものです 。

printfの特殊記号

具体的にprintf関数を呼び出した場合の出力例を示します 。

#include <stdio.h>
int main(void)
{
    long    num1 = 12;
    short   num2 = 53;

    printf("num1の10進数:%d \t num2の16進数:0x%x \n", num1, num2);

    return 0;
}

実行結果は次のように表示されます。

num1の10進数:12    num2の16進数:0x35

次のように変換指定子と引数の指定の数は整合が取れている必要があり、順番にも依存していることに注意してください。

printf関数の引数説明
ナナ
ナナ

printf関数では変換指定子と引数の型・数を一致させる必要があります。

printf関数はものすごく多機能な関数なんです。調べてみると思わぬ使い方もあったりするので、勉強してみるとよいですよ。

スポンサー

関数の部品構成と使い方

関数とやらの扱い方を教えなさい!
わたくしはたくさんの関数を従えて、なんでもいうことを聞いてもらうのよ。

ナナ
ナナ

関数のイメージがいびつなものになり始めてるね・・・。
関数には部品があるから構成要素を説明するよ。関数はこれからたくさん作ることになるから、用語も含めてしっかりと覚えてね。

例)関数に対し2つの数を入力として与え、関数側では2つの数を足し算した結果を返すsum関数を定義してみます。

int sum(int para1, int para2)
{
    int calc;

    // 入力引数の2つを足す
    calc = para1 + para2;

    // 結果を呼び出し元へ返却
    return calc;
}

関数は主に3つの部品で定義を行います。「関数名」「引数ひきすう」「戻り値の型もどりちのかた」 です。呼び名は覚えてください。

関数の部品
関数の要素

引数は入力情報・戻り値は出力情報

引数の役割は、関数というサービスを動かすために必要な入力情報を受け取ることです。上記例ではpara1、para2として足し算に必要な2つの数値を受け取る引数が指定されています。

関数の戻り値は、呼び出し元に結果を出力するためのものです。calc変数をreturnすることでpara1とpara2が足し算された結果を出力しています。

関数の引数と戻り値
ナナ
ナナ

「引数は入力情報、戻り値は出力情報」と呪文のように唱えて覚えてください。これは関数を使う上での必須事項なんです。絶対に忘れてはなりません。

return文の役割は関数の処理を終了すること

return文は関数の中で実行するとその時点で関数処理を終了し、呼び出し元へ処理が移動します。

また、return文を関数の中で1つしか書けないと勘違いする方がいますが、そんなことはありません。次のようにいくつも書くことができます。

関数の制御
ナナ
ナナ

return文は関数内に複数記載することができます。処理の途中で異常を検知したら、処理を中断するためにreturnするなんてことはよくあります。

戻り値の型とreturnの関係

戻り値の型としてvoid型以外のlong型などを指定した場合は、return命令に変数や数値を追記する必要があります。

戻り値の型とreturnで指定したものは同一のデータ型とする必要がある点に注意が必要です。

戻り値の型とreturn命令

return命令で指定した変数や値は、関数の呼び出し元に返却することができます。

戻り値のデータ型とreturnで返却するデータの型は一致させろ!

引数の与え方と戻り値の受け取り方

関数を呼ぶときの引数と戻り値のデータの受け渡しは次のように行います。

関数の呼ぶ側と呼ばれる側

関数を呼ぶためには関数名を記載し、入力に必要なデータを引数として渡します。

引数が複数ある場合は並べた順にデータが渡されますので記載する順番は意識しましょう。関数からreturnされた戻り値は変数への代入処理で受け取ることができます。

引数の値渡しとは

関数を呼び出した側が引数として渡した変数の中身は、関数側で変数の中身を書き換えても呼び出し元の変数には影響がありません。

値渡し

このように呼び出し側の変数に影響を与えない引数の渡し方のことを「値渡し」と呼びます。

上記の変数①と変数②は同一の変数名inputですが、定義された関数が異なるため、次のように異なるメモリにラベルが貼られています。変数名は同じですが、異なる変数として扱われるため値を書き換えても影響がないのです。

値渡しのメモリイメージ

戻り値の参照方法

次のプログラムはsubfunc関数を呼び出し戻り値が「-1」の場合はERRメッセージを出力するプログラムです。

// 戻り値を管理する変数定義
short ret;

// 関数からの戻り値をret変数へ代入
ret = subfunc(input);

// ret変数の内容をチェック
if (ret == -1)
{
     // -1だった場合はERRを画面に出力
     printf("ERR");
     return 0;
}

このように関数からの戻り値をいったん変数に格納してから値をif文でチェックする方式は一般的ではあるのですが、変数をわざわざ定義することが面倒なこともあります。

そのような場合、次の書き方で変数定義を省略することも可能です。

// 関数の戻り値が-1かどうかをチェック
if (subfunc(input) == -1)
{
    // -1だった場合はERRを画面に出力
    printf("ERR");
    return 0;
}

このように、if文の条件式の中で関数呼び出しを行い、ダイレクトに戻り値を比較演算子でチェックすることも可能です。変数定義を行わなくてもよくなったのがわかりますね。

ナナ
ナナ

この関数の呼び出し方と戻り値のチェック方法は、実践で非常によく使われるテクニックです。使いこなせるようになっておきましょう。

関数の引数と戻り値の型で使えるvoid型

これまでに変数の型としてchar型やshort型などの型を指定しました。

関数定義では引数と戻り値に型の指定が必要となりますが、void型という特別な型を指定することができます。

void型

引数や戻り値の情報が不要な場合に指定する型のことvoid型ぼいどがたと呼ぶ。

皆さんはこれからプログラミングを行っていく中で様々な関数を作ることになります。

関数定義を考える時には引数と戻り値をどのような情報とするか検討する必要があります。

その際に「引数はなくてもいい」「戻り値はいらない」といった場面もあります。そんな時に指定するのがvoid型です。

次のように関数の引数や戻り値の型としてvoid型を指定します。

void subfunc(void)
{
    printf("Hello");
    return;
}

引数のみをvoid型に指定、戻り値のみをvoid型に指定といったことももちろん可能です。

ただし、void型は変数の型としては使えません。次のようなプログラムはビルドエラーになります。

void subfunc(void)
{
    // void型の変数は定義できない
    // ビルドエラーが発生
    void data;

    printf("%d", data);
    return;
}
スポンサー

関数定義時によくやってしまう注意事項

関数を作ったらビルドエラーが出て何が原因だかわからないわ!あなた、原因がなにかを調べるのよっ!

ナナ
ナナ

自分で原因を調べるのも技術向上のステップだよ。でも、よくやってしまう注意事項があるから伝えておくよ。

初めて新しい関数を定義すると思わぬビルドエラーで時間を使ってしまうことがあります。よくやってしまう注意事項を解説しておきましょう。

関数定義の中で関数定義はできない

C言語では関数定義の中で別の関数を定義することは認められていません。

次のようにプログラムする方がいますがビルドエラーになることを知っておきましょう。

//  func関数の定義
void func(void)
{
    //  関数subfuncの定義はできない
    long sum(long num1, long num2)
    {
        return num1 + num2;
    }

    printf("Hello");

    return;
}
ナナ
ナナ

このような関数内で別の関数が定義できるプログラミング言語もありますが、C言語ではできないんです。

関数定義の順番に注意しよう

ソースファイルの中に関数を複数定義する場合は関数定義の並び順に注意しましょう。
次の書き方はビルドエラーが発生します。

関数定義順のエラー

このビルドエラーを回避する方法は2つあります。

関数定義順エラーの回避方法

規模の小さいプログラムを作る際は定義順で回避することもありますが、本格的なシステム開発ではプロトタイプ宣言により回避します。

プロトタイプ宣言の詳細については別章にて解説します。

スポンサー

関数と変数の関係

あなた、意外とおしゃべりね。こんなにたくさんの情報覚えられないわよ。関数のお話はまだ続くのかしら?
記事の量のわりに私の登場回数が少ないのも気になるわね。

ナナ
ナナ

もう少しだけ頑張ろうね。関数を使いこなすにはたくさん覚えることがあるんだよ。それだけプログラムにおいて関数って大事なものなの。これを身につけないとプログラミングはできないと思ってね。

じゃあ関数と変数の関係性について教えるよ!

次は「関数」と「変数」の関係性を理解しましょう。

ローカル変数とグローバル変数

関数の中で定義された変数のことをローカル変数(内部変数)と呼び、関数の外側で定義された変数のことをグローバル変数(外部変数)と呼びます。

ローカル変数とグローバル変数の定義場所

この2つの変数の違いは変数ラベルを参照できる範囲(スコープ)が異なります。

ローカル変数参照範囲

ローカル変数は定義された関数内からのみアクセスが可能であり、他の関数からは参照ができません。

このように参照範囲が「局所的」であることからローカル変数と呼びます。局所的であるがゆえに、異なる関数であれば同じ名前の変数名でも定義が可能となるのです。

グローバル変数の参照範囲

ローカル変数に対してグローバル変数にはこのような制約がなく、どの関数からでもアクセスが可能です。

関数という枠に縛られず、「広域的」にアクセスできることからグローバル変数と呼びます。

関数呼び出しによる変数のラベル剥がしのタイミング

変数を定義するということはメモリにラベルを貼ることであると解説しましたが、このラベルは剥がされることがあります。

関数の中で定義されたローカル変数は関数が呼ばれると同時にラベルが貼られ、関数の処理が終わると共にラベルが剥がされます。再度関数が呼ばれるとまたラベルが貼られ、終わると剥がされる・・・を繰り返します。つまり変数ラベルには寿命が存在するということです。

変数の寿命

ラベルが一瞬でも剥がされるということは、その後に別のラベルが貼られる可能性があるということです。つまり、関数呼び出しが終わるとローカル変数の値は保持される保証はないのです。

これに対しグローバル変数はシステムが動き出すとラベルが貼られ、その後は剥がされることがありません。そのためグローバル変数は関数呼び出しに依存せず値を保持することができるのです。

ローカル変数に記憶した値は関数呼び出しが終わると消失する!
グローバル変数に記憶した値はシステム動作中は常に保持され続ける!

スポンサー

関数をデバッグするための3つのステップ実行

関数にわたくしのお願いを言ったのに、全然いうことを聞かないわ!
全然魔法のランプじゃないじゃないの、この役立たずっ!

ナナ
ナナ

まぁまぁ、少し落ち着こうね。
そもそもちゃんとお願いは伝わってるのかな?曖昧な指示じゃ関数は願いを聞いてくれないよ。指示を出す側も出される側も意思疎通ができて初めて関数というサービスが実行されるんだよ。

関数を作ったけど思い通りに動かない、そういったことは関数を学んだ直後はよくあります。

デバッガには関数の動作を見るための機能も含まれています。それでは、ステップ実行について解説しましょう。

Visual Studioを含め統合開発環境に搭載されたデバッガにはステップ実行の種類として次のものが存在します。

  1. ステップイン
  2. ステップオーバー
  3. ステップアウト

各ステップ実行のコマンドはVisualStudioでは次のアイコンが対応しています。

ステップ実行

関数の中に入るためのステップイン

ステップインは次に実行対象のプログラムが関数呼び出しの場合、関数側へステップを移動するためのコマンドです。呼び出し先の関数をデバッグしたいときに利用します。

ステップイン

関数の中にIN(入る)するってことですね。

関数を実行したことにするステップオーバー

ステップオーバーは実行対象のプログラムが関数呼び出しであろうと、関数側には移動せずに次の行へステップを移動するコマンドです。ステップインとの違いは関数呼び出しのとき以外は同一の挙動となります。

ステップオーバー

関数の外に出るためのステップアウト

ステップアウトは実行中の関数処理を進め、呼び出し元までステップを移動させるコマンドです。

ステップアウト
ナナ
ナナ

プログラムはこの3つのステップ実行機能を使えば動く流れを把握することができます。絶対に使いこなしたいデバッグ機能がこのステップ実行です。

ステップ実行を使いこなしプログラムが動作する流れを把握せよ!

スポンサー

Q&A:関数に関するよくある質問

ナナ
ナナ

関数は結構大変でしたね。質問あれば答えますよ。

Q:今までプログラムを書いてきたmainって関数だったの?

わたくしが今までプログラミングしてきたのはmainと書かれた場所だったけど、よく見たらこれ関数じゃないの!
関数は呼ばれて動くものなんでしょ。main関数は誰が呼ぶのよ?

ナナ
ナナ

その通り、よく気づいたね。みんなが今まで作っていたのはmain関数なんだよ。main関数はC言語では必ず用意する特別な関数なんだ。

ここまで皆さんがプログラミングした処理は、mainという関数名の中に書いていたことがわかります。

C言語においてmain関数は他の関数にはない特別な役割が与えられています。

main関数とはプログラムが動き出す時に自動で呼ばれる最初の処理であり、プロジェクト内に必ず1つ存在する必要があります。

main関数の役割

皆さんがシステムを作るときは必ずmain関数を用意する必要があるということです。

Q:変数は全部グローバル変数でいいのでは?ローカル変数とかいらないんじゃないの?

グローバル変数だのローカル変数だの、なぜわたくしが意識しなくてはならないの?世界的に活躍するわたくしに使われるのだから、全部グローバルでいいのよ。ローカルなんてなくしてしまえばいいんだわ!

ナナ
ナナ

グローバル変数はシステム全体から誰からも見られる存在だから便利な反面怖い部分もあるんだよ。その点、ローカル変数は狭い範囲からしか見られないからこそ、安全面という点において強みがあるんだよ。

一見便利なように感じられるグローバル変数ですが、グローバル変数というものは安易に定義するものではありません。

どこからでもアクセスできるというのは危険性を伴うということなのです。

基本はローカル変数で定義し、どうしてもグローバル変数にするしかない場合のみグローバル変数に格上げするというスタンスがよいでしょう。

Q:関数定義の引数において、省略された書き方はOKなのか?

わたくし、関数の定義をこんな風に書いてしまったけどビルドが通ってしまったわ。これはわたくしだからこそ許されると思ってよいの?

// 引数が省略された関数定義
int subfunc()
{
    return 0;
}
ナナ
ナナ

これは誰が書いてもビルドエラーは一応でないよ。ただしC言語において引数の記載を省略した場合は特別な意味を持つから、省略はしない方がいいよ。

C言語において引数の型を省略した場合はvoid型とは異なる扱いとなります。

void型を明記した場合は明確に引数がないことを示しますが、型の記載を省略した場合は引数がなんでもよいと解釈されます。

引数が不要な場合は、明示的にvoid型を指定してあげてください。

Q:関数名の付け方に悩みます。考え方のコツは?

言うことを聞かせるための関数を作りすぎたわ。名前を何にしたらいいの?
いっそのこと、じいや1、じいや2、じいや3、じゃダメなのかしら?

ナナ
ナナ

じいやさん、悲しむよ。君も番号で呼ばれたくないでしょ。
面倒くさがらずに関数名はしっかりと考えて付けてあげてね。名は体を表すっていうでしょ。名前がその関数のサービスを表すんだよ。

関数や変数の名前の付け方に悩む人は多いでしょう。

関数とはサービスを提供するものです。まずは作ろうとしている関数がどのようなサービスを提供するのかをしっかりと考えることです。

例えば「名前を取得する」「お金を支払う」「座標をセットする」など具体的にどんなことをサービスとして提供しているのかを見極めます。それを後は英語に置き換えます。

関数名の付け方の例
  • 名前を取得する ⇒ getName
  • お金を支払う ⇒ payMoney
  • 座標をセットする ⇒ setPosition

このように関数の場合は動詞を最初に持ってくることが慣習になっています。

じいや、ごめんなさい。ちゃんと大切にするわ。
そういえばわたくし、じいやの名前知らないわ。じいやとしか呼んだことないんだもん。

Q:関数定義の際に引数や戻り値はどんな情報にするべき?

関数にどんな内容の指示を与えて報告をもらうのかを考えるのにも疲れたわ。テンプレートみたいなものはないのかしら?引数と戻り値のセオリーが欲しいわっ!

ナナ
ナナ

関数の引数と戻り値は、関数を作成する設計者によって無限のバリエーションがあるよ。だからこそ考えるのは大変なんだね。
引数に関してはサービスに必要な情報を考えて与えるしかないね。戻り値は一般的にサービスの成否を報告させることが多いね。

まずは引数に関しての考え方になります。

関数をサービスとして捉えた場合、引数はサービスを引き受けるために必要な情報を定義します。次の質問を自分自身に投げかけてみるとよいでしょう。

引数を決めるために考えるべきこと
  • 今あなたはどのようなサービスを関数として定義しようとしていますか?
  • そのサービスとは、どのような情報が伝われば成立するのですか?

続いて、戻り値の考え方です。

関数の戻り値は一般的に要求したサービスが、正当に受理されたかどうかを表す数値を返却することが多いです。

次のようにサービスが正常に受け付けられたときには0という数値を戻り値で返却することで「受理できました」ということを通知します。

関数の戻り値正常

しかし、関数をサービスとして捉えた場合、不正な注文をされるケースもあり得ますね。

次のようにサービスとして提供していない注文がされた場合、サービスの提供を拒否するという意味で戻り値に負値を返却するといったことはよく行われます。

関数戻り値不正

関数を呼び出した側は戻り値の数値を確認することで、サービスの要求が受理されたかを知ることができます。

このようにあたかも会話しているかのように、関数の引数と戻り値の情報を定義することをイメージするとよいでしょう。

Q:関数を定義するときに気をつけるべきこととは?

20年振りの休暇をじいやにプレゼントしたいと思って、じいやの代わりに願いを聞いてくれる関数を作ってあげたわ!じいやの仕事って大変なのね。関数が3,000行くらいになっちゃったわ。

ナナ
ナナ

今の段階で3,000行の関数を作れたんだとしたら、それはそれで才能だね。でも、関数を作るときに気を付けてほしいことは、あまり処理を書きすぎないことだね。

プログラミングに慣れない方がやりがちなこととして、関数に処理を詰め込みすぎることがあります。

関数の中の処理は目安として、多くても100行程度までを上限として作るとよいでしょう。

それ以上の行数になってしまった場合は、処理の中で別の関数にまとめられる処理がないかを検討しましょう。

行数が多いということはその関数の複雑さが大きくなっていることを示し、メンテナンス性が悪く将来的な拡張が難しくなっていきます。

スポンサー

課題:関数の作り方と使い方が学べたかを確認しよう

ナナ
ナナ

もしも、プログラムが上手く動かなくて困ったときは、答えを見るのではなく「デバッガ」の使い方を学びましょう。

この記事を見ると問題の解決技術が身に付きます。困ったときのオススメ記事です!

課題1

課題内容

次の関数を作成せよ。

課題1_1

main関数から上記関数を呼び出し面積を取得せよ。
また、取得した面積は出力期待結果に従い画面に表示せよ。


出力期待結果

底辺:15 高さ:20 面積:150
底辺:350 高さ:480 面積:84000

main.c

#include <stdio.h>

unsigned long getArea​Triangle(unsigned long bottom, unsigned long height)
{
    unsigned long area;

    // 面積を計算
    area = bottom * height / 2;

    // 面積を戻り値で返却
    return area;
}

int main(void)
{
    unsigned long bottom = 15;
    unsigned long height = 30;
    unsigned long area;

    // 関数呼び出し
    area = getArea​Triangle(bottom, height);

    // 面積を表示
    printf("底辺:%d 高さ:%d 面積:%d", bottom, height, area);

    return 0;
}
ナナ
ナナ

関数の一番基本となる構成です。関数と引数と戻り値を理解できているかを問うています。

この問題が解けなかった方は、もう一度本記事を読み直しましょう。最初はわからなかった内容でも読み返すことで理解できることって多いんです。

課題2

課題内容

次の関数を作成せよ。

課題2_1

main関数から上記関数を呼び出し階乗値を取得せよ。
また、取得した階乗値は出力期待結果に従い画面に表示せよ。


出力期待結果

数:0 階乗:1
数:6 階乗:720
数:10 階乗:3628800

main.c

#include <stdio.h>

unsigned long getFactorial(unsigned short number)
{
    unsigned long  calc = 1;
    unsigned short i;

    // 順に加算
    for (i=1 ; i <= number ; i++)
    {
        // 自己代入で加算
        calc *= i;
    }

    // 加算結果を戻り値とする
    return calc;
}

int main(void)
{
    unsigned short num = 10;

    // 関数の戻り値を直接表示
    printf("数:%d 階乗:%d", num, getFactorial(num));

    return 0;
}
ナナ
ナナ

関数に対してサービスを要求するという感覚を身につけましょう。本関数では階乗を知りたいmain関数が、getFactorial関数に対して「階乗を算出して教えてほしい」と依頼しているかのようにイメージすることです。

これが関数というサービスなのです。


課題3

課題内容

次の関数を作成せよ。

課題3_1

main関数から上記関数を呼び出し正方形の記号を表示せよ。


出力期待結果

lenが3の時

###
###
###

lenが5の時

#####
#####
#####
#####
#####

main.c

#include <stdio.h>

void displaySquare(unsigned short len)
{
    unsigned short i,k;

    // 2重ループで横と縦に表示
    for (i = 0 ; i < len ; i++)
    {
        for (k = 0; k < len ; k++)
        {
            printf("#");
        }

        // 1行終わったら改行
        printf("\n");
    }

    return;
}

int main(void)
{
    unsigned short length = 3;

    // 段数3で表示要求
    displaySquare(length);

    return 0;
}
ナナ
ナナ

これまでに習った反復処理と関数を組み合わせた課題ですね。過去の内容も含めて身に付いているかを問うている課題です。

このあたりの課題をさくっと解けるようであれば、ここまでの記事や関数を理解していると考えてよいでしょう。

課題4

課題内容

次の関数を作成せよ。

課題4_1

次のmain関数をプログラムを動かし、出力期待結果通りに表示されることを確認せよ。

int main(void)
{
    //  おはようございます
    echoGreeting();

    //  こんにちは
    echoGreeting();

    //  こんばんは
    echoGreeting();

    //  おはようございます
    echoGreeting();

    return 0;
}

出力期待結果

おはようございます
こんにちは
こんばんは
おはようございます

main.c

#include <stdio.h>

//  グローバル変数で定義
//  挨拶の番号
char    echoNo = 0;

void echoGreeting(void)
{
    switch (echoNo)
    {
    case 0:
        printf("おはようございます\n");
        break;

    case 1:
        printf("こんにちは\n");
        break;

    case 2:
        printf("こんばんは\n");
        break;
    }

    // 呼び出し後に次の挨拶へ
    echoNo++;

    // 最初に戻す
    if (echoNo >= 3)
    {
        echoNo = 0;
    }

    return;
}

int main(void)
{
    //  おはようございます
    echoGreeting();

    //  こんにちは
    echoGreeting();

    //  こんばんは
    echoGreeting();

    //  おはようございます
    echoGreeting();

    return 0;
}
ナナ
ナナ

関数の呼び出しが終わっても数値を保存しておきたいならグローバル変数を使うとよいです。2つ以上の関数から参照もできますよ。

echoNoで挨拶の番号を保持していますね。グローバル変数をどんな時に使うべきかを正しく判断できるようになりましょう。