swap 関数は呼び出された親の関数の 2 つの変数の内容を交換する関数である.
親の関数の変数を直接書き換えるにはポインタを使う必要がある.swap に
書き換えたい変数 2 つへのポインタ(変数のアドレス)を引数として渡せば
swap 内での処理が可能となる.例えば親の関数の変数 a, b の内容を入れ替えたいとき,
swap(&a, &b);
を実行すればよい,となるように swap 関数を
書けば良い.リスト 7.9 の swap 関数の部分を読んで理解せよ.
swap 関数をテスト実行するプログラムを作れ.
swap 関数のように独立性の高いモジュール(関数などのまとまったプログラム部分) の場合は特に厳密な外部仕様, すなわち入力,出力とその関係を記憶するぐらい理解することが重要. 理解した後は内部構造は忘れるぐらいの気持ちで扱うとよい. 外部仕様は以下のようになる.
関数の戻り値は無いので狭い意味では出力は無し,副作用として 値の入れ替えがある,とも言えるが,計算結果の外へ影響という広い意味で 出力として扱ったほうがわかりやすい.
関数 bubbleSort も独立性の高いモジュールである.外部仕様は以下のとおりである.
教科書リスト 7.9 の関数 bubbleSort 内の 2 重ループの動作を 理解するためにループ実行中の状況を図 1. に示す. 簡単のため 図 1. の実行例では長さ 4 の配列を考え, リスト 7.9 における size の値を 4, 配列 a の初期値を 4,3,2,1 としている.
図 1.2重ループのトレース
swap 関数が bubbleSort 関数の中でプロトタイプ宣言されていることにも注意.
関数 bubbleSort が配列のサイズを引数として受け取ることに注意. これは配列を扱う関数の典型的な手法である. 関数 bubbleSort はこれにより任意のサイズの配列をソートできる. bubbleSort の中にサイズを書いてしまうと汎用性が失われる.
グローバル変数にはメリットとデメリットがある.
sizeof 演算子を使うと配列名から総バイト数がわかる. 総バイト数を配列一要素のバイト数から配列長を コンパイル時 に求めることができる. リスト 7.10 を入力,実行せよ.
sizeof 演算子は引数として変数名,型名,定数値をとることができる ことに注意せよ.
ポインタに適用できる演算は限られている.
ポインタ演算は一見ふつうの整数演算に見えるので注意が必要. ポインタ演算として 3000 + 2 の結果が 3008 になる例が 教科書 p.259 の図 7.6,7.7 である.
ポインタ演算は配列を指しているポインタに対してしか意味が無い.
void へのポインタと通常のポインタの違いを理解せよ. データの占めるメモリ上の先頭アドレスを保持する点では同じだが, 通常の型へのポインタであればコンパイラがデータのサイズを知っている のに対し,void へのポインタではそれがわからない.
配列を処理するプログラムはポインタで表記する事ができる. それにはメリットとデメリットがある
ポインタ添字表記法を理解せよ. bPtr[1] は *(bPtr + 1) の省略記法である.結果的に b[1] を意味する. p.263 のリスト 7.12 の 4 種類の記法で同じものが参照できることを理解せよ.
リスト 7.13 の関数 copy1 と関数 copy2 の終了条件には注意を要する. for 分の「継続テスト」の部分が == ではなく = であることに注意せよ. なぜコピーが終了すると ループが終了するのか熟考せよ.文字列の最後には必ずヌル文字 '\0' が保存されており,その値が 0 であることを利用している. 値 0 は C 言語では真理値「偽」として扱われる.
引数 s2 が定数データへのポインタとして宣言されているが,その目的を考えよ.