情報処理II

情報処理Ⅱ
2005年12月2日(金)
本日学ぶこと


関数と変数
目的


してはいけないこと



関数を自分で定義し,変数の利用方法・範囲を明示的に制限
することで,適切な機能分割(モジュール化,再利用)を図る.
main関数のみで100行以上のプログラム
グローバル変数を駆使するプログラム
問題

並べ替えられる?
4 7 3 8 6 1 2 5
1 2 3 4 5 6 7 8
2
関数(Function)

関数の分類



自作関数: 自分で定義する.⇒ 本日のテーマ
ライブラリ関数: 出来合いのもの.printfなど.
なぜ関数を定義するのか?



処理を共通化(一般化)する
プログラムの見通しをよくする
main関数がないとプログラムは動かない
3
関数定義の方法

構文: 型名 関数名(引数並び) {文...}




型名は,関数の戻り値の型.値を返さないときは,voidと書く.
引数並びは,0個以上の「型名 変数名」をカンマで挟んだもの.
引数がないときは,何も書かないか,voidと書く.
例: double myatof(const char *str0) {…}
例: void procedure(int x, int y) {…}
一括の変数宣言と異なり,
このintは省略できない.
4
関数の呼び出し


関数定義の例:
double f(double x) {return x+1;}
関数呼び出しの例:b=f(a);
変数aの値を引数として,



関数fを呼び出し,その戻
x を関数 f の仮引数(formal
り値を,変数bに代入する.
argument),a を関数 f の
実引数(actual argument)と言う.これらを区別する必要
のないときは,ともに引数(argument)という.
x = a; の代入を行ってから,関数本文の処理に入る.関数
の処理が終われば,変数 x (のオブジェクト)は消滅する.
x = f(x); と書いてもよい.このとき,仮引数の x と,実
引数の x は,別のオブジェクトである.
5
return

関数処理中に return 値; があれば,そこで関数の処理
を終え,値を戻り値(return value)とする.


値の前後にカッコは不要.
戻り値の型がvoidなら, return; と書ける.
6
引数の授受

Cの関数呼び出しでは必ず値渡し(call by value)にな
る.


値渡し: 実引数のコピーが仮引数に格納される.その後,仮
引数の値を変更しても,実引数の値には影響しない.
参照渡し(call by reference)をしたければ,ポインタ
値を引数とすればよい.

参照渡し: 仮引数の値を変更すれば,実引数の値もそれに変
わる.Cではこの意味での参照渡しをすることができないが,ポ
インタ値を渡すことで,その参照先の値を変えることができる.
• ポインタ値とは…配列変数の名前,または変数に「&」をつけ
たもの
7
ポインタ値の値渡し

関数:


void print_message(char *message)
{
printf("%s\n", message);
 実引数の値(ポイン
}
呼び出し側:

char wakayama[] = "wakayama";
print_message(wakayama);
message
wakayama
タ値)は変更不可.
 参照先(文字列
"Wakayama"の
中身)は変更可.
関数print_messageの中
'W' 'a' 'k' 'a' 'y' 'a' 'm' 'a' '\0'
8
main関数の型

main関数の(戻り値の)型は,int とする.


void main とする本も多いが,規格上適切ではない.
正常終了は return 0; と書き,
異常終了は return 1; と書くのが一般的.
9
関数定義の順番



呼び出す関数は,呼び出す前に(プログラムファイルの上の
ほうで)宣言されていなければならない.
宣言や定義がない場合は,int 関数名(); とみなして呼
び出しを試みる.
対策


呼び出す順序に注意して関数を並べる.
関数プロトタイプを使用する.
10
関数プロトタイプ
(Function prototype)

構文: 型名 関数名(引数の型の並び);




「関数原型」
ともいう
セミコロンを忘れずに
「引数の型の並び」は,「引数(型と変数)の並び」でもよい.こ
のとき変数名は無視される.
一般に,グローバル区間に記述する.
例: int swapcase(int);
関数プロトタイプを用いることで,


関数定義の順番を気にすることなくプログラムを記述できる.
• ただし,関数プロトタイプは,関数定義の前に書くこと.
関数の入出力が明確になる.
11
変数(Variable)・識別子・オブジェクト


識別子(Identifier): 変数名,関数名,型定義名など
の「名前」
オブジェクト(Object)と識別子の違い



識別子は,プログラムファイル(静的)で記述される「ラベル」
オブジェクトは,プログラム実行中(動的)に生成される「実体」
同一の識別子に対して複数のオブジェクトが生成されることも
ある.
12
グローバル変数とローカル変数

各変数は,定義された位置によって,有効範囲(scope)を
持つ.


グローバル変数
• あらゆるブロックの外で定義された変数.
• 有効範囲はファイル末尾まで.
「ブロックの中」とは,
ローカル変数
{ と }で
挟まれた領域のこと
• ブロックの中で定義された変数.
• 有効範囲はブロック終了まで.
関数の仮引数も
ローカル変数


グローバル - global - 大域的 - あらゆるブロックの外
ローカル - local - 局所的 - あるブロックの中
13
識別子の宣言に関するルール




グローバルに同一の識別子を複数宣言できない.
一つのブロック内に,同一の識別子を複数宣言できない.
関数はブロック内で定義できない.必ずグローバル区間での
定義となる.
ブロック内に変数を定義するときは,それより外にある同一
の識別子と重複してもよい.


ブロック内では,外にある同一の識別子は使用できない.
モジュール化に関して有用なルール.
14
型の属性

記憶域クラス


型修飾子


extern, static, auto, register
const, volatile
例: extern void function1(const char *);
int x(void) {static int c=0; ...}
15
記憶域クラス(1)

auto: そのオブジェクトの生存期間は自動記憶域期間で
ある.


static: そのオブジェクトの生存期間は静的記憶域期間
である.


積極的にautoを書くことはない.
必要なときに使う.
extern: 他の場所で宣言された識別子を使用する.


分割コンパイルで不可欠.
externとstaticは,関数に
対しても指定できる.ただし
staticの意味は異なる.
プログラムが複数のソースファイルで
構成されることがある.大規模プログ
ラミングでは当たり前だが,本授業で
は実例を出さない.
16
記憶域クラス(2)

静的記憶域期間 (static変数)





staticを指定した場合や,グローバル変数が該当する.
プログラムの実行に先立ち,オブジェクトが生成され,初期値が設定
される.プログラム終了まで破棄されない.
初期値を指定しないオブジェクトには 0 が代入される.
初期値は,コンパイル時に計算可能な定数式でなければならない.
自動記憶域期間 (auto変数)




autoを指定した場合や,staticやexternの指定なくブロック内で
定義した変数と,関数の仮引数が該当する.
宣言文を実行するたびに,オブジェクトが生成され,初期値があれば
毎回初期化される.ブロックを終えると,破棄される.
初期値を指定しないオブジェクトの初期値は不定.
初期値は任意の計算式でよい.
17
有効範囲?記憶域クラス?

ここで問題





グローバルなstatic変数は,定義《 できる ∥ できない 》.
グローバルなauto変数は,定義《 できる ∥ できない 》.
ローカルなstatic変数は,定義《 できる ∥ できない 》.
ローカルなauto変数は,定義《 できる ∥ できない 》.
ローカルなstatic変数の用途



ブロック内で情報を保存しておき,あとでまた実行するときに利
用する.
動的に確保することなく,配列領域を戻り値とする.
auto変数では扱いきれない大容量オブジェクト(100万個の
int配列など)を取り扱う.
18
配列のauto変数

関数:
void print_message(void)
{
char message[] = "Wakayama";
printf("%s\n", message);
}
関数print_messageの中
message
'W' 'a' 'k' 'a' 'y' 'a' 'm' 'a' '\0'
19
変数の有効範囲の補足


破棄されるまでは,ブロックの外からでも(ポインタなどで間
接的に)オブジェクトの参照や値の書き換えができる.
⇒ ポインタによる参照渡しが可能となる.
関数内のauto変数に対応するオブジェクトは,関数処理が
行われるたびに生成される.
⇒ 再帰関数が構成できる.
関数print_messageの中
message
wakayama
message[0] = 'w';
としてよい
'W' 'a' 'k' 'a' 'y' 'a' 'm' 'a' '\0'
20
型修飾子



const: その識別子の値はプログラムによって変更できな
い.参照は可能.
volatile: 処理系が知らないうちに値を変える可能性が
ある.参照は可能.
constの使用例:

char *strcpy(char *dest, const char *src);
*dest は
関数内実行中
左辺値にできる
*src は
関数内実行中
左辺値にできない
dest, srcともに
関数内実行中
左辺値にできる
21
並べ替え問題


処理対象:intの1次元配列
処理前:
4 7 3 8 6 1 2 5
処理後:
1 2 3 4 5 6 7 8
並べ替えは「ソート(sort)」,「ソーティング(sorting)」と
も呼ばれる.
22
並べ替え問題:定義する関数

int get_array_length(int *array);



void print_array(const char *message,
int *array);


配列の要素数を求める.
× sizeof(array)/sizeof(array[0])
メッセージと,配列の各値を出力する.
void sort_by_selection(int *array);


選択法を用いて並べ替える.
配列の中身を書き換える.
23
関数の呼び出し関係
main
print_array
sort_by_selection
get_array_length
24
まとめ



関数を自分で定義することができる.関数を呼び出すとき,
引数は値渡しで授受される.
変数には,型とは別の属性として,記憶域クラスと型修飾子
を指定できる.static変数とauto変数とでは,大きく挙動
が異なる.
「変数の定義」と「オブジェクトの生成」は別.
オブジェクトの生成・破棄のタイミングに注意する.
25