Chapter8 関数の作り方
これまで学んできたキーボードからのデータの入力や処理結果の表示に使ったscanfやprintfの命令は、実は関数といわれるものである。あるまとまった処理をするためにいくつかの命令を組み合わせた小さなプログラムを関数という。C言語には標準関数という便利な多くの関数が用意されている。また、ユーザが処理したい目的に合わせて関数を作ることもできる。この関数をユーザ関数という。
これまでにもすでにswap関数など作成し使ってきたが、ここでは、体系的に関数について紹介し、自分でいろいろな関数を作成する方法を学び、これらの関数を組み合わせて大きなプログラムを組む方法を学ぶ。
標準関数
<math.h>で定義されている関数
acos(x), asin(x), atan(x), ceil(x),cos(x),exp(x),fabs(x),floor(x),log(x),log10(x),pow(x,p),sin(x),sqrt(x),tan(x)
ユーザ関数
ユーザ関数は、ヘッドとボディでできている。例えば、
int cube(int x)
{
return x*x*x;
}
において、int cube(int x)がヘッドで、return-type name(parameter-list)の形をとる。ボディは
{ return x*x*x; }である。
ユーザ関数を定義したら、すぐにその関数をテストしてみる。このテストに用いるプログラムをテスト ドライバという。テスト ドライバは”quick and dirty”でよい。つまり、普通のプログラムのような、ユーザプロンプト、出力用のラベル、ドキュメントの必要はない。
例題8.1
2つの値のうち大きいほうを返すmax( )関数のテストドライバを作成しよう。
#include<iostream.h> int max(int x, int y) { if (x < y) return y; else return x; } int main() { int m,n; do { cin >> m >> n; cout << “max(“<< m << “,” << n << “) = “ << max(m,n) << endl; } while (m != 0); } |
実行例
この例では、メイン関数の前にユーザ関数のすべてが表示されている。これは、最も単純なプログラムの書き方で、テストドライバにはこれでもよい。
もっと一般的な書き方は、関数のヘッダだけメイン関数の前に書き、ヘッドとボディをメイン関数の後に書く方法である。これにより、関数を別々にコンパイルすることができ、大きなプログラムへと発展できる。
例題8.2
上で定義したmax( )関数とメイン関数を別々にコンパイルしてみよう。
test_max.cpp
int max(int, int); int main() { int m,n; do { cin >> m >> n; cout << “\t max(“ << m << “,” << n << “) = “ << max(m,n) << endl; } while (m != 0); } |
max.cpp
int max(int x, int y) { if (x < y) return y; else return x; } |
UNIXでは
>> gcc -c max.c
>> gcc -c test_max.c
>> gcc -o test_max test_max.o max.o
>> test_max
と打つ。
例題8.3
学校名、所在地を表示するプログラムを作成しよう。ただし、学校名、所在地を表示するそれぞれの関数を作成して使用する。
実行例
考え方
学校名を表示する関数をschool、所在地を表示する関数をaddressとする。これらをmain関数から呼び出して使用するプログラムを書けばよい。
#include <stdio.h> void address(void); { school(); address(); } void school(void) { printf("広島工業大学\n"); } void address(void) { printf("広島市佐伯区三宅2-1-1\n"); } |
解説 学校名を表示する関数schoolは、schoolが呼ばれたら学校名を表示すればよい。同様にaddressも住所を表示すればよい。これらの関数を呼び出すにはmain関数でschool( )、address( )とすればよい。
このプログラムを実行すると、main関数から呼び出された関数schollに制御が移り、学校名を表示する。次に制御はmain関数に移り、今度は関数addressを呼び出して住所を表示する。
関数school(void)のように( )の中にvoidと書いた関数を引数のない関数という。
例題8.4 データを読み込んで和と差を求める
main関数から実数データa,bを関数sumと関数diffに渡して、その和と差を計算して表示するプログラムを作成しよう。
実行例
考え方
1.2つの値x,yの和を計算する関数をsum、差を計算する関数をdiffとすると、関数sumとdiffは次のようになる。
float sum(float x, float y) { float s; s = x+y; return (s); } |
float diff(float x, float y) { float d; d = x-y; return (d); } |
2.main関数から読み込んだa,bの値を関数sumに渡す。同様に関数diffに渡す。
3.計算結果はreturn文を使ってmain関数に戻す。この戻される値を戻り値という。この戻り値は実数なので、関数の前にfloatがついている。ではプログラムを書いてみよう。
#include <stdio.h> float
diff(float, float); { float a,b; printf("2つの数字を入力してください\n"); scanf("%f%f",&a,&b); printf("入力した2つの数%fと%fの和は%fです\n",a,b,sum(a,b)); printf("入力した2つの数%fと%fの差は%fです\n",a,b,diff(a,b)); } float sum(float x,float y) { float s; s = x+y; return (s); } float diff(float x,float y) { float d; d = x-y; return (d); } |
実引数と仮引数
関数sum(a,b)を呼び出すときに使用する( )の中のa,bを実引数という。また、呼び出される関数sum(float x,float y)の中のx,yを仮引数という。仮引数x,yには実引数の値a,bが渡されて計算に使用され、その結果をreturn文でmain関数に戻す。このとき、実引数と仮引数の型は同じでなければならない。このように実引数の値を仮引数に渡す方法を値呼び出し法(call by value)という。この方法では仮引数の値を実引数に渡すことはできないし、戻せる値は1つである。では、仮引数を実引数に渡すにはどうすればよいのだろうか。その答えは、参照による呼び出し(アドレス渡し)を行うことである。
これまで、数々の関数を作成してきた。新しいプログラムを作成するとき、すでに作成した関数を使いたいことがある。ここでは、すでに作成した関数を再利用する方法を紹介する。
1. 例題8.4で用いた関数sumとdiffを別々に名前を付けて保存しておく。ここでは、sum.cとdiff.cとしておく。次に、main関数だけをreidai8-4.cとして保存し、次のようにコンパイルしてみよう。
gcc reidai8-4.c sum.c diff.c -o reidai8-4.exe
すると、例題8-4と同じ結果を得ることができる。このように、Cプログラムでも、同時に2個以上のプログラムをコンパイルすることができる。
2. まず、yokota.hというヘッダファイルを作成し、sum.cとdiff.cをyokota.hにコピーしておく。次に、yokota.hをCドライブのprogramというフォルダに格納したとする。その後、main関数に#include “C:\program\yokota.h”を追加しておく。ここで次のようにコンパイルしてみよう。
gcc reidai8-4.c -o reidai8-4.exe
すると、例題8-4と同じ結果を得ることができる。このように、Cプログラムでは、自分の作成した関数を必要なときにインクルードすることができる。
例題8.5 データを配列に読み込んで平均を求める
配列a[ ]にn個のデータを読み込み、データの平均を求めて表示するプログラムを作成しよう。ただし、データの平均は関数ave_dataで求めるものとする。また、データの個数nも入力するものとする。
実行例
考え方
main関数で配列aにデータを入力し、そのデータを平均値を求める関数ave_dataに渡す。配列とポインタとは兄弟のようなものであることを7章で学んだ。つまり、配列aを関数ave_dataに渡すには配列aのアドレスを渡してやればよい。配列aのアドレスは配列名aまたは先頭アドレス&a[0]である。つまり、main関数の中で
ave = ave_data(a,n);
と書き、関数ave_dataを
float ave_data(float x[ ],int n)
{
float s=0,
int i;
for (i = 0;i < n;i++)
{
s = s + x[i];
}
return (s/n);
}
とすれば、main関数の中でave_dataが呼ばれたとき、ave_dataは配列aのアドレスと読み込んだデータの数nを仮引数x[ ]とnに渡す。その後、関数ave_dataは平均を計算し、その値をmain関数に戻す。では、プログラムを書いてみよう。
#include
<stdio.h> #define MAX 10 void main(void) { int i,n; float ave,a[MAX]; printf("データの個数を入力してください\n"); scanf("%d",&n);
{ printf("a[%d] =",i); scanf("%f",&a[i]); } ave = ave_data(a,n); printf("%f\n",ave); } { float s=0; int
i; { s = s+x[i]; } return s/n; } |
例題8.6 2つの数a,b を読み込み、main関数からa,bをユーザ関数bin_opに渡して、和、差を計算し、その結果をmainに戻して表示するプログラムを作成しよう。
実行例
考え方
例題8.4との違いは1つの関数で和と差を計算させその結果をmainに戻すことである。つまり、ユーザ関数bin_opは和、差を計算し、その結果をmainに戻さなければならない。値呼び出し法では、return文を使って1つの値しか戻すことができない。そこで、main関数の実引数は、変数のアドレスとし、ユーザ関数bin_opの仮引数は渡されたアドレスを格納できるように、ポインタを対応させる。もう少し詳しく説明しよう。
Main関数内で
bin_op(a,b,&sum,&diff);
printf(“%f%f”,sum,diff);
と書き、ユーザ関数を
void bin_op(float x, float y, float *add, float *subtract)
{
*add = x + y;
*subtract = x - y;
}
と書くと、実引数a,b,&sum,&diffが仮引数x,y,*add,*subtractに渡される。その後、x+yの結果がポインタ変数*addの中に格納されているアドレスを番地とする変数sumに代入される。よって、printfでsumの値が表示されることになる。関数bin_opは値を返さないのでvoid
bin_op(float x, float y, float *add, float *subtract)となる。では、プログラムを書いてみよう。
#include <stdio.h> { float a,b,sum,diff; printf("2つの数字を入力してください\n"); scanf("%f%f",&a,&b); bin_op(a,b,&sum,&diff); printf("入力した2つの数%fと%fの和は%fです\n",a,b,sum); printf("入力した2つの数%fと%fの差は%fです\n",a,b,diff);
} void bin_op(float x,float y, float *add, float *subtract) { *add = x + y ; *subtract = x - y; } |
C言語は、システム言語として生まれたので、文字列を操作する標準関数が用意されている。
下に挙げる関数を用いるには、ヘッダファイルstring.hをインクルードする必要がある。
関数名 |
機能 |
strlen (string lengthの略) |
文字列の文字の個数を求める |
strcmp (string comparisonの略) |
ある文字列と他の文字列を比較する |
strcpy (string copyの略) |
ある文字列を他の文字列に複写する |
strcat (string concatinationの略) |
ある文字列に他の文字列を連結する |
/* strlen関数: sの長さを返す*/ int strlen(char s[ ]) { int i;
i = 0; wile (s[i] != 0) ++I; return i; } |
“hello
world”と”ハロー ワールド”の文字数を求めるプログラムを作成しよう。
考え方
文字列str1[ ]に”hello world”を代入し、文字列str2[ ]に"ハロー ワールド"を代入する。次に、関数strlenに引数として配列名str1とstr2を渡し、計算結果を表示すればよい。では、プログラムを書いてみる。
/*文字列の長さの計算*/ #include <stdio.h> #include
<string.h> { int n1,n2; const char str1[ ] = "hello world"; const
char str2[ ] = "ハロー ワールド";
n2 = strlen(str2); printf("文字列1の文字数=%d\n",n1); printf("文字列2の文字数=%d\n",n2); } |