C言語のあれこれ4(関数の作成方法)
はじめに
以下の記事の続きになります。
以下の順番で作成していきます。
- 変数と定数の作成方法
- 基本的な値型の種類と構造体の作成方法
- 関数の作成方法 <=今回の記事
- 計算するための演算子の説明
- 条件に関する演算子と条件分岐、繰り返し文の作成方法
- 配列を伴わないポインタの使用方法
- 配列を伴ったポインタの使用方法
- プリプロセッサの利用方法
追記
前回の指摘を受けたことで、学校で習ったものと習っていない物の差を痛感しました。
やはり最低限一度は自分でも調べてみたほうが良いと実感しました。
記事を作成する時間もできたので、投稿を再開するため、
以降もおかしな点があれば指摘していただけると幸いです。
追記2
2025年の2月ごろに会社でFlutterの開発準備から開発方法を教える機会があったため、こちらの記事も頑張って作成します。
応援よろしくお願いします。
本編
ここでは関数の作成方法に関してを記述します
関数の内容
関数は基本的に次の内容で構成されます。
戻り値の型 関数名(引数の型 引数名, 引数の型 引数名...)
引数の型と引数名を合わせて1つの引数を指定できます。
戻り値の型には値型や構造体を指定できますが、戻り値を利用しない場合はvoidを指定します。
関数から抜ける場合はreturn
文を利用します。
この時、戻り値の型がvoid
以外の場合はreturn
を必ずつける必要があり、戻り値の型と同じ型の値を返す必要があります。
以下は関数の定義になります。
void Test1()
{
return;
}
int Test2(int test)
{
return test * test;
}
プロトタイプ宣言
プロトタイプ宣言は先にこの関数があることを伝えるために記述します。
プロトタイプ宣言は複数定義することができます。
関数内で利用する関数は先にその関数があることを伝える必要があります。
そのため関数の定義としてプロトタイプ宣言と呼ばれるものを行います。
関数の内容を記述する場所は一か所のですが、プロトタイプ宣言は何回も記述することができます。
int Test3();//プロトタイプ宣言したTest3関数//
main関数
エントリーポイント
(プログラムの開始地点)になります。
C言語では基本的にmain関数
がエントリーポイントに当たります。
Windowsのデスクトップアプリケーションを開発している人は基本的にWinMain
がエントリーポイント
になるため、注意してください。
int main()
{
Test1();
int test = Test2(5);
int test2;
test2 = Test3();
return 0;
}
※1.C言語はmain関数の返す値でアプリケーションの正異常判定を行います。
そのため、戻り値が0の場合は正常終了、0以外の場合は異常終了と認識されます。
Test2(5)
と関数を利用している部分ですが、Test2
の中で利用するint test
と引数で渡した5の値は別の変数になります。
動きとしては、Test2の開始時に新しい変数を用意して5を代入する'になります。 これを
値渡しと言います。
値渡し`ではTest2に引数として渡されたものが変数の場合でも、その変数の値が変わることはありません。
しかし、ポインタを渡す場合は話が変わります。
ポインタを渡す場合はポインタ渡し
と呼ばれます。
ポインタ渡しではポインタ自体は値渡しですが、ポインタの指す先は同じものになるため、関数内でポインタ先の値を変動すると、関数で使用したポインタ先の値も変動されます。
最後にすべての内容を結合して動かすコードにすると、以下の通りになります。
void Test1()
{
return;
}
int Test2(int test)
{
return test * test;
}
int Test3();//プロトタイプ宣言したTest3関数//
int main()
{
Test1();
int test = Test2(5);
int test2;
test2 = Test3();
return 0;
}
int Test3()
{
return 500;
}
Discussion
タグに C++ も入っていますが C と C++ は規則がかなり違います。 また、規格の改定もあるので資料を見るときはどの版のことを想定しているのかを意識しないと混乱することもあります。
C ではこの
Test3
はプロトタイプ (関数原型) のない関数宣言として解釈されます。 プロトタイプがないというのは型情報がない扱いであるという意味です。型情報がないのでコンパイラが型チェックを充分に行わないことがあり、しかし型の不整合があって良いわけではないので間違いがあるとコンパイラが検出しないままデタラメな動作をするかもしれません。
仮引数リストが空の場合はそういう解釈になるので引数を受け取らない関数は
というように
void
を書く必要があります。C++ ではプロトタイプのない関数宣言という概念はなく、
void
を書いても書かなくても単に仮引数を受け取らないプロトタイプ宣言として解釈されます。 また、 C でも 2023 年の改定からは C++ と同様の解釈になるように変更されたのですが、現時点でそれをあてにするのは時期尚早だと思います。原則としてはそうすべきであるのは間違いないのですが、 C では
return
を通過せずに}
に到達して、なおかつ関数の呼び出し元で返却値を使うことが未定義であるということになっています。 逆に言えばreturn
がないだけなら一応は有りです。 あえてそんなことをする理由もないので豆知識というか与太話レベルのことですが。ちなみに C++ では
return
を通過せずに}
に到達したらそれだけでただちに未定義です。関数の返却値の型が
return
に渡される式の型と一致しない場合は代入時と同じ規則で暗黙の型変換が試みられます。 暗黙の型変換が可能であればそのように型が調整されるので型が完全に一致しなければならないというわけではないです。0
かEXIT_SUCCESS
の場合は成功、EXIT_FAILURE
の場合は失敗、それ以外は処理系定義であるということになっています。詳細はホスト環境 (OS) の都合によるのでどんな値でも構わないというわけではないです。 たとえば POSIX では下位 8 ビットのみがホスト環境に渡されるので大きな値を返せません。
早速コメントありがとうございます。
特にこちらはVisual C/C++を利用しているとわからない内容ですね。
ありがとうございます。
また、
こちらに関しては完全に初見でした。
記事も見つけたため、以下に添付して覚えておきます。
改めてコメントしていただきありがとうございます。