練習問題4: 終端記号 END をもつ整数型配列内のデータを,他の整数型配列へ複写する関数を作れ。今回は,関数名を含めて関数仕様も考えることが問題である。
関数名は, data を copy するということで, dataCopy とした。
引数の変数名 src は source(送り元)の略であり, dest は destination(行き先)の略である.
void dataCopy(int src[], int dest[]) { int i; for (i = 0; src[i] != END; i++) { dest[i] = src[i]; } dest[i] = END; }
この関数内での for 文では, src[i] の値が終端記号 END になる直前まで dest[i] に src[i] を複写している.このままでは,dest には終端記号がついていないので,for 文が終わってから付加している.
仮引数においては,何々型配列は何々型ポインタと同じである. したがって,引数に渡された配列をポインタとして扱って次のようにコーディングすることもできる.このようにすると,配列の添字として用いた変数 i が不要となる.
void dataCopy(int *src, int *dest) { while (*src != END) { *dest = *src; src ++; dest ++; } *dest = END; }
src ++ や dest ++ は,ポインタが指す配列中の要素位置を,一つ後ろにずらす効果がある.
この while 文でも終端記号は複写されないので,while の終了後,dest に END を付加する必要がある。
「ループの後で END を付加するのは面倒だ.ループの中で src の終端記号もまとめて dest に複写すれば良いのに」と思った人は,次のように書けば良い.
void dataCopy(int *src, int *dest) { while ((*dest = *src) != END) { src ++; dest ++; } }
while の継続条件部分にある *dest = *src という代入式は,代入した値を,値としてもつことに注意しよう.すなわち, *src の値を *dest に代入して,その値で while 文を続けるかどうかを判断している.したがって,END が複写された時点で while 文が終了する.
for 文を用いても,同様のコーディングができる.
void dataCopy(int *src, int *dest) { for (; (*dest = *src) != END; src ++, dest ++); }
この for 文は,初期設定部分がなく,継続条件部分で判断の前にデータの複写を行い,再設定部分には src と dest とを次の要素にずらすという二つの実行式があり,繰り返し本体部分がない.
このソースコードは短いが,ここまで短縮すると,見たときに意味を読み取るのが難しくなるので,あまりお勧めはしない。
この関数の使用例を挙げておく。
main() { int a[] = {4, 2, 3, 6, 3, 2, 1, 3, 5, 7, -1}; int b[20]; int i; dataCopy(a, b); for (i = 0; b[i] != END; i++) printf("%3d", b[i]); printf("\n"); }
4 2 3 6 3 2 1 3 5 7
ここで作った関数 dataCopy(src, dest) の引数は, src, dest の順に並んでいる.これは,「src から dest にコピーする」という言葉の順序に従うように決めたからである.英語で表現しても, "Copy data from src to dest." という順序になる.
一方,標準ライブラリにある,文字列を複写する関数 strcpy(dest, src) の引数は, dest, src の順に並んでいる.このようになった理由は, a = b という代入文では,左側に代入先が,右側に代入元がある,というである.
どちらの順序が良いのかというのは,意見が分かれるが,自分で書いたプログラムはどちらかに統一しておくべきである.