概ね方位不定だが多分割と偏っている
スポンサーサイト
--年--月--日 (--) | 編集 |
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。


C 関数プロトタイプ
2006年08月26日 (土) | 編集 |
最初の頃に単語だけは出てきたが、詳しくは後で、と言われていた定義だな。
過去の負債の1つを消化する時が来たようだ。

面倒そうで手に余りそうだが…

関数の型は、特に宣言しなければint型とみなされるんだが、実数型やポインタ型などのint型以外の値を返す関数はその関数を呼び出す前に必ず型を宣言しておかなくてはならないらしい。
更に、ANSI Cではその際に関数の引数の型についても宣言するよう勧めているそうだ。こういった、関数の使用に先立って関数の型と引数の型を宣言したものを関数プロトタイプと呼ぶわけだな。

関数プロトタイプ宣言がなされている場合は、関数呼び出し時に引数の型に誤りがあったとしてもコンパイラがエラーメッセージを出して教えてくれるそうな。

例えば、

double abs(double);
void main(void)
{
 a=abs(1);
}

double abs(double x)
{
 何か式
}


といったプログラムの場合、最初のプロトタイプ宣言(double abs(double)の部分)で宣言されたdoubleという型と、main関数内の関数absの引数である1が一致しない(1はint型故)のでコンパイラがエラーを吐いて教えてくれるという寸法だ。

また、上記の例ではmain関数の前にプロトタイプ宣言を行っているが、
今までやってきたように、

double abs(double x)
{
 何か式
}

void main(void)
{
 何か式
}


関数の定義をメイン関数よりも先に書けばプロトタイプ宣言と同じ効果を得られる。
が、関数が少ないうちはこれでいいんだが、関数が増えてくると色々と面倒臭くなるから関数プロトタイプをプログラムの先頭にまとめて置く方が実用的だそうだ。それなら最初から関数プロトタイプを最初に置くような教え方してくれればいいのにと思わなくもないが。

さて。
printfやscanf等、予めライブラリとして提供されているような標準ライブラリ関数でも関数定義部分をソースレベルでは持たないので、関数プロトタイプ宣言を行う必要があるが、これら標準ライブラリ関数のプロトタイプ宣言はそれぞれのカテゴリ別にインクルードファイルで行われているので、これを取り込むことによってプロトタイプ宣言が行われたことになっている。
今まで訳分からないままも記述してきた#include<stdio.h>というのがそれだな。
また、これまた今まで訳分からないままに記述してきたvoid main(void)だが、これ(void)は値を返さない関数の型を意味し、更に(void)は引数を持たない関数、ということも意味している。


以上で形式は終わり。
実践に移ろう。

与えられた文字列を反転し、それへのポインタを返す関数を作ってみる。

今回は文字数を求める必要があるらしく、文字列の長さを調べるというそのまんまな関数strlenを扱う為にstring.hというファイルをインクルードする必要がある。

#include<stdio.h>
#include<string.h>


最初からして今までと違うな。
続いて文字列を反転する関数reverseを記述する。

char *reverse(char a[ ])

char *でポインタを返し、文字列aの先頭アドレスを引数として渡すという宣言

{
 static char buf[80];
 int i,n;
 n=strlen(a);
 for(i=0;i  buf[i]=a[n-i-1];
 buf[n]='\0';
 return(buf);
}


反転した文字列を格納する為の文字列bufを宣言し、
ループ変数として使うi、文字数を格納するnを宣言し、
nに文字列a[ ]の文字数を代入し、
ループ文でbuf[ ]にa[ ]の後ろの文字から代入していく。
a[n-i-1]なのは例えば5文字の文字列ならばa[0]~[4]に格納されている為。
これだとi=0から始まり、i<5で、i++の時

buf[0]=a[5-0-1]=a[4]
buf[1]=a[5-1-1]=a[3]
buf[2]=a[5-2-1]=a[2]
buf[3]=a[5-3-1]=a[1]
buf[4]=a[5-4-1]=a[0]

となり、見事に反転した文字列がbuf[ ]に格納されるが、a[n-i]だとi=0の時、b[0]=a[5]でa[5]は\0であり、配列の最初にいきなり\0が格納されてしまうことになる。
ここが納得できれば後は簡単で、b[n]で最後に文字列の終わりを意味する\0を格納し、return(buf)で配列bufの先頭アドレスを返して終わり。

で、次はメイン部分。

void main(void)
{
 char str[80];
 printf("適当に文字列を入力したまえよ。ただし半角英数で");
 scanf("%s",str);
 printf("%s\n",reverse(str));
}


そもそもの文字列を宣言し、scanfで入力し、そしてその文字列の先頭アドレスを引数としてreverse関数に渡して、その結果である反転した文字列を表示する。

func_proto_test.jpgfunc_proto_test2.jpg
こんな感じ

おまけ:
今回、strlen関数を使ったが、実はstdio.hだけでも定義可能。

int len(char *a)
{
 int n=0;
 while(*a!='\0'){
  a++;
  n++;
 }
 return(n);
}


文字列a[]の先頭アドレスを引数として渡し、
カウント用に変数nを宣言し、
文字列a[]を終わりである\0までカウントしつつ繰り返し、
最後にそのカウントnを返すことによって文字数を求める事ができる。

なので、

func_proto_test3.jpgfunc_proto_test4.jpg
こんなんでも同じ結果を得られる

ううむ奥が深い。

まとめよう。

関数を使う前に関数の型と引数の型を宣言したものを関数プロトタイプと呼ぶ
戻り値ではなく引数の型で宣言することに注意
voidは戻り値を持たない(値を返さない)、引数を持たない、を意味する
a[]はa*でも同じ意味。前にもやったが


こんなところか。
これで関数の章も終わり。
理解が足りているか不安だが…後は実際にやってどうにかするしかないな。
スポンサーサイト

コメント
この記事へのコメント
コメントを投稿する
URL:
Comment:
Pass:
秘密: 管理者にだけ表示を許可する
 
トラックバック
この記事のトラックバックURL
この記事へのトラックバック
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。