◆◆◆◆アマゾンで購入する◆◆◆

10.関数の話 
           内    容
(1)こんなとき
(2)擬似コードの話
(3)作業単位としての関数
(4)受け渡すための機構
(5)結果を受け取るための機構
(6)関数記述は,詳細⇒概略
(7)ソースプログラムの分割
(8)ヘッダファイルの分割


(1)こんなとき

今, あなたは,
A社に書籍を届けなければならないとします。

しかし,他のスケジュールが詰まっていて,
届けるどころか梱包する時間もありません。

困ったあなたは,この仕事を
X君に任せることにしました。

C0: X君,ここの本をA社のHさんに届けてくれないか?
X0: いいですよ。A社ってどこにあるんですか?
C1: B町のJビル4階だよ。
X1: B町って初めてですけど。
C2: 地下鉄A駅の東口から右に出て
3分ほど歩いたところ。
大きなビルだから,すぐ分かるよ。
X2: そうですか。A駅ってどう行けば?
C3: D駅からJR線のF行きに乗って,
E駅で乗り換え,G行きに乗ればいいよ。
X3: そうそう,これ梱包しますか?
C4: そうだねー,会社の封筒にでも入れて,
封してヒモかけてくれないか?

以上の会話では,
会話が進むにつれて 細かな話になり,
最後は梱包の話まで進んでいます。

あなたがX君に頼みたい仕事を構成図で描くと,
以下のようになります。




二人の会話では,

●最初,やるべきこと(What)を
 大まかな言葉で指示し,

●相手が分からなければ,
 どうやれば(How)よいかを細かく指示

しています。

プログラミングも同様,

@ 何をやるかを整理あるいは決定(What)
         : 機能決定

A 決めたことがプログラミング言語の
  1文で表現できなければ,
  どう処理するかを決める(How)
         : 制御構造の決定

の手順で行います。

上記Aでは,これまで説明した制御構造の
どれかを使って, より細かい機能の並びを決めます。

それぞれの機能の並びに対して,
更に@,Aを繰り返します。

(2)擬似コードの話

Cの勉強をやるときは,
問題の整理をやる際の制御構造はCを使い,
機能要素は日本語で書きましょう。

これをソフトウェア工学では,
Cの擬似コードで書く」という言い方をします。



(3)作業単位としての関数

以下の作業は,
仕事を依頼されたXさんが,さらに仕事を分割して,
別の人に仕事を頼むための
作業単位と考えることができます。

 ●書籍を梱包する
 ●荷物をA社まで運ぶ
 ●Hさんに渡す

ただし,Xさんには次の作業が残ります。

 ●書籍を梱包する人に書籍を受け渡し
 ●書籍を梱包する人から梱包された荷物を受け取り
 ●A社まで運ぶ人に梱包された荷物を受け渡し
 ●A社まで運ぶ人から運ばれた荷物を受け取り
 ●Hさんに渡す人に運ばれた荷物を受け渡す

作業者に「受け渡し」て,
その結果を「受け取る」という作業が残るわけです。

なお,イメージ的には,
荷物の運び人を連れ歩いているような場面を
想定すると分かりやすいでしょう。

(4)受け渡すための機構

受け渡しを行うためのCの機構を
「引数」と呼びます。

引数を指定するには,以下の記述が必要です。

@依頼者が,実際のもの(実引数と呼ぶ)を
 受け渡す部分の記述

A作業者がその作業を行うために,
  受け取ることができるもの(仮引数と呼ぶ)の
  種類の記述。

@とAに矛盾があってはいけません。
水を飲みたいときに,
同じ容器だからといって竹製のカゴを渡されたら?
だめですね。水が入るような容器でなければなりません。

この他,
依頼者も作業者両方ともに
アクセスできる場所に受け渡すものを置き,
作業者はそれを受け取る方法もあります。
(静的変数の項を参照)

ただし,両者が,
その場所を約束事として
取り決めておく必要があります。


(5)結果を受け取るための機構

受け取るための機構には,
以下の2通りがあります。

@作業が終わったら,手渡してもらう。
A作業結果を置く場所(参照渡しの引数)を指定し,
  そこに置いてもらう。依頼者は,作業終了後,
  適当なときに取りにいく。

(4)と同様,Aに似た方法として
依頼者も作業者両方ともに
アクセスできる場所に置く方法も,

このときも,両者が,
その場所を約束事として
取り決めておく必要があります。

■作業者から手渡してもらう方法=リターン値

この方法では,次のように行います。

@呼び出された側では,
  呼び出した側に対してReturn文で返却する。

A呼び出した側では,関数呼び出し結果の値として受け取る。

[例]
  [呼び出し側]

     int a, x; x = 20; a = set_twice(x);

  [呼び出される関数側]

    int set_twice(int x) { return x*2; }

■受け取る場所を指定する方法 =参照渡しの引数

@ 呼び出す側では,
  引数がポインタ(アドレス)であることを指定します。
  
  ポインタであることを示すには,
  引数の先頭に&を付けます。

   [例1] int a;
       set_10(&a);

  なお,配列では,その名称を書くと
  ポインタが指定されたものとみなされますので,
  特に&は付けません。

   (みっともない文法ですがこれは決まりですので覚えてください)

   [例2] char ch[10];
       set_string(ch);

A 呼び出される関数側に,
  ポインタまたは配列であることを指定します。
  ポインタの場合は,仮引数に*を付けます。

   [例1] set_10(int *a)
       { *a = 10;  }

  配列では,配列であることを示す[]だけを記述し,
  サイズは記述しません。

   [例2] set_string(char ch[])
       { ch[0]=‘a’; }

■受け取る様子の観察

それでは,簡単なプログラムで
受け取る様子を確認してみましょう。

言語の勉強をする際には,
このように,簡単なプログラムを書いて
動かしてみるのが理解に役立ちます。

  [例題プログラム]
#include "stdafx.h"
char *ptest1(char b[],char ch,int i)
{ b[i]=ch; return b; }

char *ptest2(char b[],char ch,int i)
{ b[i]=ch; return &b[i]; }

static char a[10] = "abcdefg";

int main(int argc, char* argv[])
{ printf(“\n-------配列ポインタ引渡し----");
 printf("\n[テスト1]ptest1(a,'X',2)");
 printf("\n 開始データ a = [%s]\n",a);
 printf(“\n 結果データ return = [%s]\n",
          ptest1(a,'X',2));

 printf("\n (副作用)  a = [%s]\n",a);
 printf(“\n[テスト2]ptest1(a,’*',4)");
 printf("\n 開始データ a = [%s]\n",a);
 printf("\n 結果データ return = [%s]\n",
          ptest2(a,'*',4));

 printf("\n (副作用)  a = [%s]\n",a);
}


    [実行結果例]
-------配列ポインタ引渡し----"
テスト1]ptest1(a,'X',2)

  開始データ   a = [abcdefg]


  結果データ return = [abXdefg]

  (副作用)    a = [abXdefg]

[テスト2]ptest1(a,’*',4)");

  開始データ    a = [abXdefg]

 結果データ return = [*fg]

  (副作用)    a = [abXd*fg]


(6)関数記述は,詳細⇒概略?

第9章の演習問題を,関数を使って
書き直してみました。

処理のまとまりがはっきりしていますね。

  [関数記述]
#include "stdafx.h"
int *sort_int(int dx[])
{
 int i,j, d;
 for(i=0;i<4; i++)
 for(j=i+1;j<5;j++)
   if(dx[i]>dx[j])
   { d=dx[i];dx[i]=dx[j];dx[j]=d;}
 return dx;
}

/* voidは値を返却しないことを示す */
void array_print(char title[],int d[])
{
 int i; printf("%s = { %d",title, d[0]);
 for(i=1;i<5;i++) printf(", %d",d[i]);
 printf("}");
}

static int dt[5]={5, 3,4,8,2};

int main(int argc, char* argv[])
{
 array_print("\n* Input Array ",dt);
 array_print("\n* Result Array ",sort_int(dt));
}


しかし,作るときは概略から詳細へと進むのに,
記述は,詳細な関数を先に書き,
メインを後の方に記述しています。

これは,使われている関数があるかないかを
チェックする必要がコンパイラにあるからです。

しかし,プログラムを見るときは,
大まかな部分から詳細な部分へと
読んでいきたいのが人情です。

しかし,コンパイラの都合もありますので,

C言語では,
関数の型や引数部分だけを先に宣言し,
(これを「プロトタイプ宣言」といいます)
本体部分だけを後で記述するような
記述方法が許されています。

  [プロトタイプ宣言]
#include "stdafx.h"
int *sort_int( int dx[]);
void array_print(char title[],int d[]);
static int dt[5]={5, 3,4,8,2};
int main(int argc, char* argv[])
{
 array_print("\n* Input Array ",dt);
 array_print("\n* Result Array ",sort_int(dt));
}
int *sort_int(int dx[])
{
 int i,j, d;
 for(i=0;i<4; i++)
 for(j=i+1;j<5;j++)
  if(dx[i]>dx[j])
  {d=dx[i];dx[i]=dx[j];dx[j]=d;}
 return dx;
}
void array_print(char title[],int d[])
{
 int i;
 printf("%s = { %d",title, d[0]);
 for(i=1;i<5;i++) printf(", %d",d[i]);
 printf("}");
}


(7)ソースプログラムの分割

ソースプログラムが大きくなると,
1箇所を変更するだけで,
全部コンパイルしなければなりません。

そこで,関数別, あるいは
いくつかの関連した関数群を
別々のファイルに保管する方法が 一般的です。

VC++6では,次のように操作します。
(VC++.Netでも同様の指定が可能です)

@ 「プロジェクト」 「プロジェクトへ追加」
  「新規作成」を選択します。



A ファイルタブでC++ソースファイルを 選択し,
  ファイル名を指定して,
  「OK」ボタンをクリックします。



B 分割する部分を入力します。
  入力後は,すべてを保存しましょう。



以下は,分割例です

(Sample1003.cpp)
   #include "stdafx.h"
int *sort_int(int dx[]);
void array_print(char title[],int d[]);
static int dt[5]={5, 3,4,8,2};
int main(int argc, char* argv[])
{
 array_print("\n* Input Array ",dt);
 array_print("\n* Result Array ",sort_int(dt));
}

(Sample1003_2.cpp)

   #include "stdafx.h"
int *sort_int(int dx[])
{
int i,j, d;
for(i=0;i<4; i++)
for(j=i+1;j<5;j++)
  if(dx[i]>dx[j])
  {d=dx[i];dx[i]=dx[j];dx[j]=d;}
return dx;
}
void array_print(char title[],int d[])
{ int i;
 printf("%s = { %d",title, d[0]);
 for(i=1;i<5;i++) printf(", %d",d[i]);
 printf("}");
}


(8)ヘッダファイルの分割

前出のファイル中にプロトタイプ記述

    int *sort_int( int dx[]);
   void array_print(char title[],int d[]);

の2行がありますが,これは

    Sample1003_2.cpp

中のインターフェース部分だけを取り出したものです。

しかし,Sample1003_2.cppを変更したら,
プロトタイプ記述も変更しなければいけません。

これを変更したら,呼び出している部分を
変更しなければなりませんが,

プロトタイプ記述は,Sample1003_2.cppの
の担当者でも変更できます。

このためには,Sample1003_2.cppの担当者が
プロトタイプ記述を変更すれば,
すべて入れ替えられるようにします。

このためのファイルをヘッダファイルと呼び,
includeで指定します。

ヘッダファイルを追加するには,
ソースファイルの追加と同様,

@ 「プロジェクト」 「プロジェクトへ追加」
  「新規作成」を選択します。

A ファイルタブでヘッダファイルを 選択し,
  ファイル名を指定して,
  「OK」ボタンをクリックします。

B ヘッダファイルにはプロトタイプ記述を入力し,
  参照側にはinclude文を入れます。

以下は,分割例です

(Sample1003.cpp)
   #include "stdafx.h"
#include "Sample.h"
static int dt[5]={5, 3,4,8,2};
int main(int argc, char* argv[])
{
 array_print("\n* Input Array ",dt);
 array_print("\n* Result Array ",sort_int(dt));
}

(Sample.h)

   int *sort_int(int dx[]);
void array_print(char title[],int d[]);

(Sample1003_2.cppは(7)に示したものと同じです)

 1.C言語の誕生

 2.C言語の仲間たち

 3.新しい言語をすばやく覚えるには

 4.Cでプログラミング

 5.データの入れ物 変数の考え方

 6.注釈・定数

 7.プログラムの実行順序

 8.配列

 9.C言語特有の代入文・制御構造

10.関数の話

11.構造体と共用体

12.ビット演算

13.プログラムの場所をポインタで

14.ファイルの入出力

15.色々な便利な方法