本日の課題

Download Report

Transcript 本日の課題

オブジェクト指向プログラミング
1回目:ガイダンスとCの復習
情報メディア学科
濱本和彦
本日の内容
学科の学習内容についてもう一度確認しよう
授業ガイダンス
先輩が作ったソフトウェアの紹介
C言語の復習



構造体
関数
ポインタ
履修モデルの再確認
履修モデル
コンセプトマップ
詳しくは4セメの「ソフトウェア設計」で勉強しますが。。
ソフトウェア開発手順:概要
企画立案
要求仕様書の作成

目的,要求の定義と分析
システム設計

プログラムの機能分割,接続方法
プログラム設計仕様書の作成

各関数の構造設計,データ構造の設計
コーディング
テストとデバッグ
保守・運用
この例でC言語を復習しましょう
例えば「住所録」ソフトの開発
要求仕様


目的:住所や電話番号の管理
要求:データの入力・保存・追加・削除・訂正・検索
システム設計


機能分割(要求をそれぞれ関数で実現):
データ入力,データ追加,データ削除,データ保存,データ読み
込み,整列,検索
接続法:
各機能(関数)へデータのアドレスを渡す
ここでは「データの入力」に限って考えます。
(その他の内容については,土曜日1限のC言語復習の
時間に勉強していく予定です)
データ入力部の実現
どのように入力するか?
キーボードからの入力
 ファイルからの入力
 他のソフトウェアからのインポート

データ構造はどうするか?
何人分ものデータを扱う=「配列」
 一人のデータにいくつもの小項目がある=「構造体」

構造体の考え方
intやcharなどの基本データ型を好きなよう
に組み合わせて,新しいデータ型として扱
います。
1つの名前だけで,関連したいくつもの変
数を一度に扱うことが出来ます。
密接な関係を持ついくつかの変数を,1つ
の構造体名の元に組織化する働きです。
例えば…
学生の名前と,英語と数学の点数を100
人分管理することを考えましょう。どのよう
に管理しますか?
char name[100][20];
int eigo[100];
int suugaku[100];
それでは…
出席番号38番の学生のデータを,配列0
番の要素にコピーするとしたら,どのような
作業になりますか?
strcpy(name[0], name[38]);
eigo[0] = eigo[38];
suugaku[0] = suugaku[38];
3行書く必要がある。間違う可能性が高くなる。
構造体を利用すれば
構造体変数gakusei[100]が宣言されてい
るとすれば…
gakusei[0] = gakusei[38];
たったこれだけで,名前も英語の点数も数
学の点数も同時にコピーが完了します!
では,構造体はどのように定義
するのでしょう?
「名前」「英語」「数学」という3つのデータを扱うための
構造体「student」を考えます。
「student型」というデータ型を新しく作る,と思ってください。
タグ
struct student{
char name[20];
};
int
eigo;
int
suugaku;
メンバ
注意:プリプロセスでの宣言になります。
では,構造体変数gakusei[100]は,
どのように宣言するのでしょう?
新しいデータ型と考えよう
struct student gakusei[100];
gakusei[]は,それぞれの配列要素が,構造体宣言
された複数のメンバ(name, eigo, suugaku)を持ち
ます。では,それぞれのメンバにアクセス(利用)す
るにはどうしたらよいでしょうか?
メンバの参照
•構造体宣言された変数,gakusei[38]のメンバeigo
を参照するには,次のように「.」を用います。
•ただし,構造体変数がポインタ変数の場合は異な
ります。ポインタの項で説明します。
gakusei[38].eigo
演習1
student型で変数を宣言し,その変数のメ
ンバにキーボードから値を入力するプログ
ラムを作成して下さい。
ensyu1.cをダウンロードして,プログラムリ
ストを追加,完成して下さい。
演習2
構造体配列を宣言し,配列の一つの要素
にメンバの値を入力し,他の要素に構造体
コピーすることにより,メンバ全てがコピー
されることを確認して下さい。
ensyu2.cをダウンロードしてリストを追加し
て下さい。
ensyu1.cの結果を利用しても良いです。
では,住所録。
住所録データとして,メンバ変数には何を
用意すればよいでしょうか? また,それは
どのようなデータ型で宣言すればよいで
しょうか?
演習のプログラムを参考にして,住所録
データを入力して,確認のために画面に出
力するプログラムを作りましょう。
address.cをダウンロードして,リストを追加
してください。
注意点
郵便番号や電話番号は数字です。小数で
はありませんが,整数型で良いでしょう
か?また,ハイフン(-)はどう処理すれば良
いでしょうか? 0で始まる場合は?
「今,何件目の入力を行っているか?」が
分かるようなメッセージが出力されれば,
より分かりやすいプログラムとなります。そ
ういった工夫も心がけましょう。
次の課題
プログラムをより見やすく記述しましょう。





入力部を,関数として宣言します。
関数へデータを渡したり,関数から処理後のデータを
受け取る方法を考えましょう。
配列のアドレスを関数とデータをやりとりするための
引数とします。
そこで,まず,ポインタの考え方を勉強しましょう。
Javaでは表向きポインタが出てくることはありません
が,実は裏ではポインタを主とした動作をする言語で
す。ポインタの動作を理解していた方がプログラムの
書き方を理解することが容易です。
アドレスとポインタ
例えば整数型の変数aが int a; と宣言されたと
します。このとき,メモリ中に,aのための領域が
確保されます。
メモリ中の「どこに」領域確保されたか,その住所
のことを「アドレス」と言います。
int a; と宣言されたとき,その整数型変数aのアド
レスは,&a で表されます。
scanf(“%d”,&a); の意味を考えてみましょう。
&の意味
scanf(“%d”, &a);は,「キーボードから入力された十進数値を,
変数aに代入してください」という意味です。
変数aの箱は,メモリ上のどこかにあるはずです。
「56」
変数aの箱
はどこ?
a
メモリの中
メモリの中のどこに変数aがあるかわからない!
&の意味
じゃ,どうすればいい?
「56」
56
a
メモリの中
メモリの中の変数a
の箱の場所
変数a様
OK!
この「住所」が
&aの意味
です!
つまり,scanf(“%d”,&a)ってどんな意味?
int a;
scanf(“%d”, a);
部屋番号
(住所)
↓
アドレス
0003
0004
0005
アドレス指定!
0006
0007
&a
メモリ空間=集合住宅
0008
a
アドレスとポインタ
例えば,int *a; と宣言された場合,aのこ
とを「ポインタ」と呼びます。
ポインタは,メモリのアドレスをポイント(指
し示す)働きをします。
ポインタが指し示しているアドレスに保存さ
れているデータ値は,*a により取り出す
ことが出来ます。
& と * は,どう違うの?
int a;
int *b;
アドレス
メモリ空間
0002
0003
b=&a;
a=5;
*b=10;
&a 0004
a ← 10
5
0005
0006
*b
!重要!
宣言
int
a;
int *p;
データ参照
アドレス参照
a
&a
*p
p
演習3
ensyu3.cを実行し,ポインタの動作を確認
しましょう。
c=&a; を実行せずに,直接 *c=56; を実
行しようとするとどうなるでしょう?
配列とポインタ
例えば,配列 int a[3]; が宣言されたとき,
a[0]~a[2]はメモリ上に連続した領域が確
保されます。
配列名aは,この配列の先頭アドレスを表
すポインタの一種です。
配列変数が宣言された場合
int a[3];
アドレス
int *b;
b=a;
a
*b=10;
b++;
*b=15;
0002
メモリ空間
0003
a[0] ← 10
a[1] ← 15
0004
a[2]
0005
0006
0007
*b
演習4
ensyu4.cを実行し,配列要素が連続領域
に確保されていることを確認しましょう。
データ値だけでなく,そのアドレスも出力す
るようにプログラムを変更しましょう。
変数の型をfloatやdoubleに変更してみま
しょう。
演習5
ensyu5.cを実行し,二次元の配列要素が,
どのように一次元メモリ領域に確保されて
いることを確認しましょう。
参考:ポインタと文字列
次の二つの文章の働きについて考えてみ
ましょう。
char
char
s[3]=“abc”;
*p=“xyz”;
上は,配列sに文字列abcが代入されます。
下は,xyzという文字列がメモリ上のどこかに書き込
まれ,その先頭アドレスがpに代入れさます。
ポインタと構造体
構造体がポインタで示されたとき,そのメン
バは,“->”によって参照されます。
struct address
struct address
friend;
*p;
p=&friend;
p->name=“Hamamoto”;
p->tel_no=“042-???”;
では,住所録プログラムへ
「構造体配列の先頭アドレスを取得し,ポ
インタを一つずつ進めながら配列要素に入
力を行う」ようにプログラムを変更しましょう。
address2.cを参考にして作成してください。
関数の働き
そもそも,「関数」って何?
何かが入力
されると・・
その入力に応じた
何かが出力される
あるルールに従って
このような関係にあるものを「関数」と呼びます。
一体,何が便利なの?


同じ処理が何度も出てくる時は,一度「関数」として作っておけ
ば,そこに入力するデータを変えるだけで結果を得られるので,
プログラムが短くて済みます。
何度も同じ処理を書かなくて良いので,プログラムがスッキリ
見やすくなります。
関数の働き
例えば,こういう働きをするものは何?
“Hello, world”
printf(
)
ですね。
つまり,
printf(
)も関数の一つなのです!
このように,プログラムは,いろんな関数の集まりでで
きています。
関数は,自分で作る事ができます!
関数の働き(まとめ)
関数とは,ある入力を与えると,ルールに従ってある結果を返し
てくれるもの
プログラムは関数の集まりでできており,言語が用意しているも
の(printfやscanf)だけでなく,自分自身で作る事もできる
main関数は特別な関数で,プログラム中に必ず存在し,この関
数からプログラムは実行されます。
つまり,main関数からいろんな関数が呼び出され,実行されます。
頻繁に繰り返される処理を関数として作っておくと,必要な時に関
数を呼び出し,入力するデータだけを変更すれば良く,プログラム
が短く分かり易くなる
関数ごとに役割を決め,関数の集まりとしてプログラムを作成す
ることにより,誰が見ても分かり易く,使いやすくなる

データの受け渡し方法など約束を決める必要がある

「printfへ入力する文は,” “で囲め」,など
簡単な関数の例
では,関数を作ってみよう!
数学における関数を思い出しましょう。
関数 f に x を与えたときの結果を y に代入する
これは,こう書きましたね。
y  f ( x)
実際に,関数 f が x をもらってどういう計算をしているかは,・・
f ( x)  x  3x  2
2
と定義しましたね。
簡単な関数の例
では,関数を作ってみよう!
プログラミング言語の場合も同じです。
f ( x)  x  3x  2
2
y  f (3)
と関数を宣言して,
のように利用します。
関数の宣言は,main関数の外で行います。
関数は,main関数など他の関数の中で利用されます。
自分自身の中で呼び出すこともできます(再帰処理)
簡単な関数の例
では,関数を作ってみよう!
#include <stdio.h>
double f( double x)
{
double y;
int main(void)
{
double x,y;
printf("x=?");
scanf("%lf", &x);
y = x*x+3*x+2;
y = f(x);
return y;
}
printf("y=%f\n",y);
}
ここが関数の宣言部です
簡単な関数の例
では,関数を作ってみよう!
#include <stdio.h>
double f( double x)
{
引数
double y;
ローカル変数
y = x*x+3*x+2;
return y;
}
関数の処理は,{ }で囲んで
表します。
プログラム内では,変数は型
指定されなければなりません。
関数が呼び出されたところか
ら受け取るデータを引数とい
います。
関数の答えが何型になるの
か,の指定も行います。
計算した後,関数の答えとし
て出力する変数の値はreturn
文で返します。
関数の中で宣言される変数を
ローカル変数と言います。関
数の中だけで有効です。
簡単な関数の例
では,関数を作ってみよう!
必ず,main関数から実行されます
#include <stdio.h>
int main(void)
{
double f( double x) 引数の値が double x,y;
渡されます。
{
両方ともロー
カル変数の
double y;
printf("x=?");
ため,変数名
scanf("%lf", &x);
は同じですが
y = x*x+3*x+2; まったく別の
変数です。
渡された引数を元に
y = f(x); 関数が呼び
処理を行います。
出されます
return y;
}
printf("y=%f\n",y);
処理結果のyが関数の結果
として呼び出し元に返されます。
}
結果が画面に表示されます。
間違いやすい例
ensyu6.cを実行してみましょう
#include <stdio.h>
void f(int x, double y){
y=0.5*x*x+3*x+0.5;
}
void main(void){
int z;
double y;
scanf(“%d”,&z);
f(z, y);
printf(“y=%lf\n”,y);
}
解決方法
#include <stdio.h>
void f(int x, double *y){
* y=0.5*x*x+3*x+0.5;
}
void main(void){
int z;
double y;
scanf(“%d”,&z);
f(z, &y);
printf(“y=%lf\n”,y);
}
関数とポインタ-演習
ensyu7.cを見てみましょう。
二つの変数の値を交換する関数です。つ
まり,二つの変数の値が変更になるため,
関数の結果はreturn文では返せません。
きちんと結果を得るためには,どのように
すればよいでしょうか?
ヒントは,ポインタです。
解説
二つの変数,aとbの値が更新されるため,
これらのアドレスを関数へ渡さなければな
りません。
関数側では,アドレスを受け取るため,引
数はポインタで宣言されます。
cを媒介変数として,データの交換を行い
ます。ポインタが指している領域の値を操
作するときは,*を付けます。
住所録プログラムへの関数の適用
address2.cを参考にして,入力部と画面への表
示部を関数としたaddress3_1.cを完成させてく
ださい。

入力部 int input(struct address *)
引数 sturct address型配列の先頭アドレス
 返り値 入力されたデータ数(整数)


表示部 void print_list(struct address *, int)
引数 struct address型配列の先頭アドレス,入力された
データ数(整数)
 返り値 なし

データ入力部
データ入力部は次の部分になります。この関数化を考えます。
a=my_address_book;
i=0;
while(1){
printf("\n%d件目\n",i+1);
printf("氏名 ");
scanf("%s",a->name);
printf("郵便番号 ");
scanf("%s",a->yubin_bango);
printf("住所 ");
scanf("%s",a->jyusho);
printf("電話番号 ");
scanf("%s",a->telno);
i++; a++;
printf("入力を続けますか? y/n : ");
scanf("%s",cd);
if((cd[0]=='n')||(cd[0]=='N')) break;
}
データ入力部の関数化
データ入力部の働きは, my_address_book[10]に
データを入力すること。
my_address_book[10]は今後他の関数でも共通
に利用する変数(データ)である。いくつの
データが入力されたかの情報は他の関数でも利
用するので,保持しておく必要がある。
my_address_book[10]の先頭アドレスを引数と
する。
入力されたデータの個数を返り値とする。
データ入力部の関数化
データ入力関数の基本構造は次のようになります。
int
{
input(struct address *a)
int
i=0;
// データを入力。a++; i++;
return(i);
}
ただし,*aは,my_address_book[10]の先頭アドレスを表す
ポインタです。
データ表示部
データ表示部は次のようになります。この関数化を
考えます。
a=my_address_book;
for(j=0;j<i;j++){
printf("\n%d件目\n",j+1);
printf("氏名 %s\n",a->name);
printf("郵便番号 %s\n",a->yubin_bango);
printf("住所 %s\n",a->jyusho);
printf("電話番号 %s\n",a->telno);
a++;
}
データ表示部の関数化
データ表示部の働きは, my_address_book[10]の内
容を画面に表示すること。
my_address_book[10]は各関数で共通だが,表示の
ためにデータの個数が引数として新たに必要とな
る。
my_address_book[10]の先頭アドレスとデータの個
数を引数とする。
「ここを先頭として何個分を出力する」という形。
データ表示部の関数化
データ表示関数の基本構造は次のようになります。
void print_list(struct address *a, int num)
{
// num回繰り返し
// データの出力,a++;
}
ただし,*aは,my_address_book[10]の先頭アドレス,numは
データの個数を表す。
関数の利用
宣言したデータ入出力関数をmain関数で利用する場合は
次のように記述します。
void main(void)
{
struct address my_address_book[100];
int
num;
num=input(my_address_book);
print_list(my_address_book, num);
}
ヘッダファイルの活用
printf()やscanf()も関数ですが,その処理を定義
している部分は見あたりません。なぜでしょう?
これら標準入出力に関する関数はstdio.h内に宣
言されており,これをincludeすることにより利用
可能としています。.hの拡張子を持つファイルを
ヘッダファイルと言います。
住所録プログラムで利用する関数をヘッダファイ
ルとして整理しておけば,これをincludeするだけ
で関数を利用することが出来ます。
ヘッダファイルの活用
address3_2.cを開いてみましょう。
同じ処理を行うプログラムが,これだけの行に整
理されています。
プログラムないで利用する関数や変数,または
変数の型は,ヘッダファイルに整理しておき,そ
れをincludeすることにより利用します。
address.hを見てみましょう。typedef文の働きを
確認しましょう。
ヘッダファイル address.h
プログラム内で共通に利用する変数,または変数の型も,ヘッダファイルに
整理しておき,それをincludeすることにより利用することができます。
typedefstruct
char
char
char
char
} address;
_address{
name[20];
yubin_bango[8];
jyusho[100];
telno[13];
int
{…}
input(address *a)
void
{…}
print_list(address *a, int num)
typedef文の働きを確認しましょう。
ヘッダファイルを利用した
プログラムの例
ヘッダファイルを利用することにより,プログラムは次のように
整理されます。< >と“ ”の違いに注意しましょう。
#include <stdio.h>
#include "address.h"
void
{
main(void)
address my_address_book[100];
int
num;
num=input(my_address_book);
print_list(my_address_book, num);
}