「C言語/関数」の版間の差分

削除された内容 追加された内容
「呼び出し先」を「子(呼び出し先)」のように記述変更し、親子関係に例える。「呼び出し先」と「呼び出し元」だけだと、どっちがどっちか、まぎらわしいので。
フリースタンディングなど難解な話題を、『C言語/中級者向けの話題』に移動した。こちらからは除去。
613 行
</source>
 
== 実行環境 ==
=== フリースタンディング環境 ===
オペレーティングシステムのいかなる支援もなしにCプログラムを実行する環境を、
フリースタンディング環境という。
フリースタンディング環境では、
プログラムの開始時に呼び出される関数の名前および型は処理系定義である。
フリースタンディング環境では、複素数型を使ってはいけない。
フリースタンディング環境でプログラムが利用できるライブラリ機能は、
<float.h>, <iso646.h>, <limits.h>, <stdarg.h>,
<stdbool.h>, <stddef.h>, <stdint.h>
であり、<ref>『JISX3010:2003』p.5「4 規格合致性」</ref>
これ以外は処理系定義である。
<ref>『JISX3010:2003』p.8「5.1.2.1 フリースタンディング環境」</ref>
 
=== ホスト環境 ===
オペレーティングシステムの支援ありでCプログラムを実行する環境を、
ホスト環境という。<ref>『JISX3010:2003』p.8「5.1.2.2 ホスト環境」</ref>
ホスト環境では、プログラム開始処理において呼び出される関数の名前はmainである(エントリポイント)。
main関数とはプログラムの開始処理において、
いろいろな初期化処理の後、最初に呼び出される関数である。
たいていのプログラムはこのmain関数から始まると考えてよい。
main関数には大まかに分けてプログラム仮引数(コマンドライン引数)を持たない関数と持つ関数との2種類の記述方法がある。
 
* プログラム仮引数とは
プログラムを実行する際、プログラムに対して渡される情報を、プログラム仮引数と呼ぶ。
例えばコマンドプロンプトにおいて、
 
<source lang=c>
C:\example.exe ABC DEF GHI
</source>
 
のようにプログラムを実行した場合、
example.exeプログラムのmain関数に対して、
「example.exe」、「ABC」、「DEF」、「GHI」という4個の文字列が渡される。
 
* プログラム仮引数を持たない関数
プログラム仮引数を使用しない場合、次のように仮引数をもたない関数を記述する。
 
<source lang=c>
int main(void)
{
/*いくつかの文*/
}
</source>
 
* プログラム仮引数を持つ関数
プログラム仮引数を使用する場合、次のように2つの仮引数をもつ関数を記述する。
<source lang=c>
int main(int argc, char *argv[])
{
/*いくつかの文*/
}
</source>
 
argcはプログラム仮引数の個数である。
argvはプログラム仮引数を表す文字列の配列へのポインタである。
argv[0]が指す文字列はプログラム名である。
argcにはこのプログラム名も個数として数えられる。
 
ただし、argcが0の場合、argv[0]は空ポインタである。
またargc>0であっても、ホスト環境からプログラム名が得られない場合、
argv[0]は空ポインタである。
<ref>『[迷信] argv[0] はプログラム名 | 株式会社きじねこ』http://www.kijineko.co.jp/tech/superstitions/argv0-is-program-name.html</ref>
argv[1]からargv[argc-1]までが指す文字列は、プログラム仮引数である。
argv[argc]は空ポインタである。
<ref>『JISX3010:2003』p.8「5.1.2.2.1 プログラム開始処理」</ref>
 
<source lang=c>
//例 プログラム仮引数
#include <stdio.h>
 
int main(int argc, char *argv[])
{
for (int i = 0; i < argc; ++i)
{
printf("argv[%d]の値 : %s\n", i, argv[i]);
}
}
</source>
 
ホスト環境では、プログラムは、全てのライブラリ機能を使用してよい。
<ref>『JISX3010:2003』p.9「5.1.2.2.2 プログラムの実行」</ref>
 
ホスト環境では、main関数の返却値が、このプログラムを実行するオペレーティングシステムに対して渡され、0はプログラムの成功終了状態を表している。
main関数を終了する}に到達した場合、main関数は値0を返す。
<ref>『JISX3010:2003』p.9「5.1.2.2.3 プログラム終了処理」</ref>
 
== 関数の応用 ==
 
=== inline ===
関数指定子inlineは、関数の宣言だけで使用できる。
関数指定子inlineは、その関数の呼び出しを可能な限り速くすることを示唆する。
この示唆が効果をもつ程度は、処理系定義とする。
<ref>『JISX3010:2003』p.83「6.7.4 関数指定子」</ref>
 
inline関数は、その関数を呼び出した部分に展開して直接埋め込む。
関数呼び出しにかかる処理を短縮することができるが、
コードを複数の部分に展開するためファイルサイズが大きくなる。
 
<source lang=c>
//例 inline関数の使用例
#include <stdio.h>
 
inline int function(int a, int b)
{
return a+b;
}
 
int main(void){
int r;
r=function(1,2);
}
</source>
 
=== 関数へのポインタ ===
関数へのポインタとは、
ある関数のメモリアドレスを格納し、
その関数に間接的にアクセスする方法である。
関数へのポインタは、
次のような場合に使われる。
ひとつは、関数を呼び出す際に、
関数へのポインタを引数として渡し、
呼び出した関数の内部で、
関数へのポインタが指す関数を実行する場合。
もうひとつは、関数へのポインタの配列をつくり、
if文や、switch文で関数を呼び出すのをやめ、
コードを単純にする場合。
 
関数へのポインタの宣言の記述は次のようになっている。
<source lang=c>
返却値のデータ型 (*関数へのポインタ名)(引数のリスト):
</source>
この宣言では、
代入する関数と同じ返却値のデータ型と引数のリストを指定する必要がある。
また、演算子の優先順位のため、
「*関数へのポインタ名」を囲う「()」を省略はできない。
 
関数のメモリアドレスを関数へのポインタに格納する記述は次のようになっている。
 
<source lang=c>
関数へのポインタ名=関数名;
</source>
 
関数名の後ろに、「()」はいらないことに注意。
 
関数へのポインタを使って、
間接的に関数を呼び出すには、
次のように記述する。
 
<source lang=c>
(*関数へのポインタ名)(引数のリスト)
</source>
 
<source lang=c>
//例 関数へのポインタの引数
#include <stdio.h>
 
void func1()
{
printf("func1()が呼び出されました。\n");
}
 
void func2(void (*func)())
{
printf("func2()が呼び出されました。\n");
(*func)();
}
 
 
int main(void)
{
func2(func1);
}
</source>
 
<source lang=c>
//例 関数へのポインタの配列
#include <stdio.h>
 
int add(int a, int b)
{
return a+b;
}
 
int sub(int a, int b)
{
return a-b;
}
 
int mul(int a, int b)
{
return a*b;
}
 
int div(int a, int b)
{
if(b==0){
printf("0で割ることはできません。\n");
return 0;
}else{
return a/b;
}
}
 
int main(void)
{
int i,j;
int arithmetic;
int (*func[])(int a, int b)={add, sub, mul, div};
printf("2つの整数をスペースで区切って入力してください。:");
scanf("%d %d", &i, &j);
printf("計算方法を入力してください(0=加法、1=減法、2=乗法、3=除法)。:");
scanf("%d", &arithmetic);
if(0<=arithmetic&&arithmetic<=3)
printf("答えは%d。\n", (*func[arithmetic])(i,j));
}
</source>
 
=== 再帰 ===
再帰とは、ある関数がその関数自身を呼び出すことである。
再帰に向いた計算に再帰を使うと、ソースコードを簡潔に書ける場合がある。
 
ある関数がその関数自身を呼び出すたびに、
引数と制御が戻るべきアドレス(リターンアドレス)が、
スタックと呼ばれる領域に格納される(プッシュ)。
また、ローカル変数もスタックと呼ばれる領域に格納されている。
関数の処理が終わるたびに、
スタックに最後に格納したデータを取り出して(ポップ)、
関数を呼び出したときの状態を復元し、
処理を続ける。<ref>矢沢久雄、原田英生『日経 BP パソコンベストムック C言語とC++がわかる本』日経ソフトウェア、2013年5月15日発行、112項</ref>
 
再帰を行うには、その関数がリエントラントである必要がある。
リエントラントであるためには、使用する変数を共有しないように気を付ける必要がある。<ref>『再入可能とは 「リエントラント」 (reentrant) さいにゅうかのう: - IT用語辞典バイナリ』http://www.sophia-it.com/content/%E5%86%8D%E5%85%A5%E5%8F%AF%E8%83%BD</ref>
 
<source lang=c>
//例 再帰を使った階乗の計算
#include <stdio.h>
 
int factorial(int n);
 
int main(void)
{
int i;
printf("整数を入力してください:");
scanf("%d", &i);
printf("%dの階乗は%dです。", i, factorial(i));
}
 
int factorial(int n)
{
if(n==0)return 1;
return factorial(n-1)*n;
}
</source>
この例では再帰を使って、入力された整数の階乗を計算している。
3を入力した場合、処理の流れは以下のようになる。
 
<source lang=c>
factorial(3)が呼ばれ、
factorial(3)が実行され、factorial(2)が呼ばれ、
factorial(2)が実行され、factorial(1)が呼ばれ、
factorial(1)が実行され、factorial(0)が呼ばれ、
factorial(0)が実行され、1が返され、
factorial(1)に戻り、1*1が返され、
factorial(2)に戻り、1*1*2が返され、
factorial(3)に戻り、1*1*2*3が返される。
</source>
 
== 脚注 ==
<references/>