PPT

情報処理Ⅱ
第8回
2004年11月30日(火)
本日学ぶこと


関数と変数
目的


してはいけないこと



関数を自分で定義し,変数の利用方法・範囲を明示的に制限
することで,適切な機能分割(モジュール化,再利用)を図る.
main関数のみで100行以上のプログラム
グローバル変数を駆使するプログラム
問題

シーザー暗号ができるか?
• ABCDEF...WXYZの各文字を
• DEFGHI...ZABCに変換する.
Wagahai
Zdjdkdl
2
関数(Function)

関数の分類



自作関数: 自分で定義する.⇒ 本日のテーマ
ライブラリ関数: 出来合いのもの.printfなど.
なぜ関数を定義するのか?



処理を共通化(一般化)する
プログラムの見通しをよくする
main関数がないとプログラムは動かない
3
関数定義の方法

構文: 型名 関数名(引数並び) {文...}




型名は,関数の戻り値の型.値を返さないときは,voidと書く.
引数並びは,0個以上の「型名 変数名」をカンマで挟んだもの.
引数がないときは,何も書かないか,voidと書く.
例: double myatof(const char *str0) {…}
例: void procedure(int x, int y) {…}
一括の変数宣言と異なり,
このintは省略できない.
4
関数の呼び出し


関数定義の例:
double succ(double x) {return x+1;}
関数呼び出しの例:b=succ(a);
変数aの値を引数として,関数



succを呼び出し,その戻り値
xを関数succの仮引数(formal
を,変数bに格納する.
argument),aを関数呼び出しの
実引数(actual argument)と言う.これらを区別する必要
のないときは,ともに引数(argument)という.
x = a; の代入を行ってから,関数本文の処理に入る.関数
の処理が終われば,変数x(のオブジェクト)は消滅する.
x = succ(x); と書いてもよい.このとき,仮引数のxと,実
引数のxは,別のオブジェクトである.
5
return

関数処理中に return 値; があれば,そこで関数の処理
を終え,値を戻り値(return value)とする.


値の前後にカッコは不要.
戻り値の型がvoidなら, return; と書ける.
6
引数の授受

Cの関数呼び出しでは必ず値渡し(call by value)にな
る.


値渡し: 実引数のコピーが仮引数に格納される.その後,仮
引数の値を変更しても,実引数の値には影響しない.
参照渡し(call by reference)をしたければ,ポインタ
値を引数とすればよい.

参照渡し: 仮引数は,実引数の別名となる.その後,仮引数
の値を変更すれば,実引数の値もそれに変わる.
Cではこの意味での参照渡しをすることができない.
7
関数定義の順番



呼び出す関数は,呼び出す前に(プログラムファイルの上の
ほうで)宣言されていなければならない.
宣言や定義がない場合は,int 関数名(); とみなして呼
び出しを試みる.
対策


呼び出す順序に注意して関数を並べる.
関数プロトタイプを使用する.
8
main関数の型

main関数の(戻り値の)型は,int とする.


void main とする本も多いが,規格上適切ではない.
正常終了は return 0; と書き,
異常終了は return 1; と書くのが一般的.
9
変数(Variable)・識別子・オブジェクト


識別子(Identifier): 変数名,関数名,型定義名など
の「名前」
オブジェクト(Object)と識別子の違い



識別子は,プログラムファイル(静的)で記述される「ラベル」
オブジェクトは,プログラム実行中(動的)に生成される「実体」
同一の識別子に対して複数のオブジェクトが生成されることも
ある.
10
グローバル変数とローカル変数

各変数は,定義された位置によって,有効範囲(scope)を
持つ.




グローバル変数
• あらゆるブロックの外で定義された変数.
• 有効範囲はファイル末尾まで.
ローカル変数
ブロックの中とは,
• ブロックの中で定義された変数.
{ と }で
• 有効範囲はブロック終了まで.
挟まれた領域のこと
グローバル - global - 大域的 - あらゆるブロックの外
ローカル - local - 局所的 - あるブロックの中
11
識別子の宣言に関するルール




グローバルに同一の識別子を複数宣言できない.
一つのブロック内に,同一の識別子を複数宣言できない.
関数はブロック内で定義できない.必ずグローバル区間での
定義となる.
ブロック内に変数を定義するときは,それより外にある同一
の識別子と重複してもよい.


ブロック内では,外にある同一の識別子は使用できない.
モジュール化に関して有用なルール.
12
型の属性

記憶域クラス


型修飾子


extern, static, auto, register
const, volatile
例: extern void function1(const char *);
int x(void) {static int c=0; ...}
13
記憶域クラス(1)

auto: そのオブジェクトの生存期間は自動記憶域期間で
ある.


static: そのオブジェクトの生存期間は静的記憶域期間
である.


積極的にautoを書くことはない.
必要なときに使う.
extern: 他の場所で宣言された識別子を使用する.


分割コンパイルで不可欠.
externとstaticは,関数に
対しても指定できる.ただし
staticの意味は異なる.
プログラムが複数のソースファイルで
構成されることがある.大規模プログ
ラミングでは当たり前だが,本授業で
は実例を出さない.
14
記憶域クラス(2)

静的記憶域期間 (static変数)





staticを指定した場合や,グローバル変数が該当する.
プログラムの実行に先立ち,オブジェクトが生成され,初期値が設定
される.プログラム終了まで破棄されない.
初期値を指定しないオブジェクトには 0 が代入される.
初期値は,コンパイル時に計算可能な定数式でなければならない.
自動記憶域期間 (auto変数)




autoを指定した場合や,staticやexternの指定なくブロック内で
定義した変数と,関数の仮引数が該当する.
宣言文を実行するたびに,オブジェクトが生成され,初期値があれば
毎回初期化される.ブロックを終えると,破棄される.
初期値を指定しないオブジェクトの初期値は不定.
初期値は任意の計算式でよい.
15
有効範囲?記憶域クラス?

ここで問題





グローバルなstatic変数は,定義《 できる ∥ できない 》.
グローバルなauto変数は,定義《 できる ∥ できない 》.
ローカルなstatic変数は,定義《 できる ∥ できない 》.
ローカルなauto変数は,定義《 できる ∥ できない 》.
ローカルなstatic変数の用途



ブロック内で情報を保存しておき,あとでまた実行するときに利
用する.
動的に確保することなく,配列領域を戻り値とする.
auto変数では扱いきれない大容量オブジェクト(100万個の
int配列など)を取り扱う.
16
変数の有効範囲の補足


破棄されるまでは,ブロックの外からでも(ポインタなどで間
接的に)オブジェクトの参照や値の書き換えができる.
⇒ ポインタによる参照渡しが可能となる.
関数内のauto変数に対応するオブジェクトは,関数処理が
行われるたびに生成される.
⇒ 再帰関数が構成できる.
17
型修飾子



const: その識別子の値はプログラムによって変更できな
い.参照は可能.
volatile: 処理系が知らないうちに値を変える可能性が
ある.参照は可能.
constの使用例:

char *strcpy(char *dest, const char *src);
*dest は
関数内実行中
左辺値にできる
*src は
関数内実行中
左辺値にできない
dest, srcともに
関数内実行中
左辺値にできる
18
暗号化問題

仕様




入力文にあるすべての'A'をα,すべての'B'をβ,…に置き換
えて出力する.(単一換字暗号という.)
文字は,char型の任意の値とする.ただし'\0'を除く('\0'
は必ず'\0'に置き換える,と考えてもよい).
どの文字をどの文字に置き換えるかは,プログラムの中で指
定する.
簡単な操作で,復号できるようにする.
Wagahai Ha Nekodearu
•
暗号化
•
復号
Zdjdkdl Kd Qhnrghdux
19
暗号化問題

(まずい)方針

'A'をα,'B'をβ,…に置き換えるのを
switch~caseで行う.
• プログラムが無駄に長くなる.
• 柔軟性に欠ける.
char encrypt_word(char c)
{
switch (c) {
case 'A':
return 'D';
case 'B':
return 'E';
...
}
}
20
暗号化問題

方針

暗号化のための変換テーブルを,配列で保持する.
• char encrypt_table[256];
encrypt_table['A']の値
encrypt_
table
… 'D'
+0
'E'
+'A'
+1
'F'
'G' …
+'C'
+'B'
+'D'
+255
21
暗号化問題

方針(続き)


暗号化は,写像を用いる.
• a = 'A'; のとき,b = encrypt_table[a]; でbの
値が'D'となるようにするには,あらかじめ
encrypt_table['A']の値を'D'としておけばよい.
復号のためのテーブルは,逆写像を用いて求める.
• char decrypt_table[256];
• decrypt_table[encrypt_table['A']] = 'A';
gがfの逆写像
であるとは,
g(f(x))=x
22
暗号化問題

fが恒等写像
であるとは,
f(x)=x
方針(続き)


定義する関数
• 文字列形式の変換テーブルを,配列に変換する.
• 復号のためのテーブルの値を設定する.
• 変換テーブルを恒等写像にする(変換のリセット).
• 変換テーブルを用いて,文字列を暗号化もしくは復号する.
暗号化と復号はどう区別する?
• ./encrypt Wakagai Ha Nekodearu
• ./encrypt -d Zdjdkdl Kd Qhnrghdux
「コマンドラインオプション」
と呼ばれる
23
まとめ



関数を自分で定義することができる.関数を呼び出すとき,
引数は値渡しで授受される.
変数には,型とは別の属性として,記憶域クラスと型修飾子
を指定できる.static変数とauto変数とでは,大きく挙動
が異なる.
「変数の定義」と「オブジェクトの生成」は別.
オブジェクトの生成・破棄のタイミングに注意する.
24