(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.色々な便利な方法
|