ぼんさい塾

ぼんさいノートと補遺に関する素材や注釈です.ミスが多いので初稿から1週間を経た重要な修正のみ最終更新日を残しています.

Cの文法 (12)

2010-09-14 14:59:34 | 暮らし
「まず覚えるCの文法」校正中)

P45: 再帰
//-------------------------------------
#include <stdio.h>
int gcd(int i, int j){
  int k;
  if(i<0 || j<0){return 0;}
  if(j<i){k=j; i=j; j=k;}
  k=j%i;
  if(k>0){
    printf("%d%%%d==%d\n", j, i, k);
    return gcd(k, i);
  }else{return i;}
}
int main(void){
  printf("i==%d\n", gcd(18, 30));
  return 0;
}
//-------------------------------------

P46: 静的な局所変数
//-------------------------------------
#include <stdio.h>
int noise(int k){
  static int n=1;
  if(k!=0){n=k;}else{n=(5*n+1)%8;}
  return n;
}
int main(void){
  int i;
  for(i=0; i<8; i++){
    printf("%d ,", noise(0));
  } 
  return 0;
}
//-------------------------------------

P47: マクロ
//-------------------------------------
#include <stdio.h>
#define N 2
double av(int *p){
  double x=0; int i;
  for(i=0; i<N; i++){x=x+p[i];}
  return x/N;
}
int main(void){
  int v[N], i;
  for(i=0; i<N; i++){
    printf("v[%d]=");
    scanf("%d", &v[i]);
  }
  printf("average=%f\n", av(v));
  return 0;
}
//-------------------------------------

P45 の gcd() はユークリッドの互除法で i と j の
最大公約数を求める関数です.

・ユークリッドの互除法の原理については Wikipedia
  等で調べてください.
・18,30 が代入された i,k は gcd() 内では通常の
  変数と同様に扱われます(#42).
・gcd(18, 30) の実行中に gcd(12, 18) が呼び出され
  18%12!=0 なので gcd(6, 12) が呼び出されます.
  このように関数が自分を呼び出すことを再帰呼び出し
  といいます.
・gcd(12, 18) を実行するとき実引数を記憶する領域が
  新しく準備されるので,gcd(18, 30) 計算時の i,k 
  の値は保存されます(#43).
・gcd(6, 12) では 12%6==0 なので「return i;」の方が
  選択され,再帰呼び出しが終了します.
・再帰呼び出しを使うとアルゴリズムが分かり易くなり
  ますが実行時間とメモリを贅沢に使います.階乗を
    int fact(int n){
      if(n>1){
        return n*fact(n-1);
      }else{return 1;}
    }
  で計算するような無駄は止めましょう.

関数内で宣言された変数(局所変数)の値は通常関数の
実行が終了すると使えなくなりますが,P46 の n のよう
に宣言すれば使えるようになります.P46 の noise() は
0 から 7 までの値をランダムに選択したように見せかけ
て戻り値とする関数です.

・線形合同法による擬似乱数の生成についてはWikipedia
  等で調べてください.
・擬似乱数の数列は noise(0) で生成します.初期値を
  設定したいときは noise(k) を使います.
・静的な変数はコンパイル時に領域が確保され,初期値が
  指定されていれば,機械語プログラムにその値が設定さ
  れます.これに対して,自動変数は関数が呼び出される
  たびにスタック上に領域が与えられ(初期値の指定があ
  れば)毎回初期化されます.
・局所変数を静的にすれば,関数外からの書き換えを防ぎ
  つつ値を保持することができます.

Cの処理系ではソースプログラムをプリプロセッサで変更
してからコンパイルします.「#」で始まる行はプリプロ
セッサに対する指令で,これまで説明しなかった
  #include <stdio.h>
は「この行を stdio.h というファイルで置換しなさい」と
いう指令です(#60).P47 のプログラムの
  #define N 2
は,マクロ N の定義で,プリプロセッサはソースプログラ
ムのこの行以降のすべての N を 2 で置換します.

・小さな N の値でプログラムをデバッグし,使うときは
  N の大きな値にしてコンパイルしなおすのが便利です.
・[#47] に示されているような引数付きのマクロも定義
  できますが,実行時間に厳しい制約がなければ引数付き
  マクロは使わない方が無難です.