第三回レポート総評




関数 sum に関して

正解例は以下の通り。
#include <stdio.h>

int sum(int n);

int main(void){

  int s;
  int n=10;

  s=sum(n);

  printf("s=%d\n",s);

  return 0;
}

int sum(int n){

  int i;
  int s;

  s=0; /* s の初期化 */

  for(i=1 ; i<=n ; i++){
    s = s + i;
  }

  return s;
}
一応プログラム全体を書いておきました.

sum 関数内で和を格納する変数 (ここでは s) を初期化していない 人が何人かいました。
自動変数は通常初期化しないと何が入っているかわかりませんませんから、
和が正しく計算されるかどうかは、処理系に依存してしまいます。よって初期化しないのは間違いです。
実際に s を初期化しないプログラムを実行すると、 「Windows+BCC」では「s=55」となりますが、「PC-Unix+gcc」上では「s=-1077938437」という結果になってしまいました。
どんな環境でも正しく動作するプログラムを書くよう心がけましょう。

他に致命的な間違いとしては以下の様なものがありました。


swap 関数に関して

正解例は以下の通り。見通し良くするために 3 つのバージョンを一つのプログラム にまとめて書きました。
#include <stdio.h>

void swap_v(int x, int y);
void swap_p(int *x, int *y);
void swap_r(int &x, int &y);

int main(void){

  int x, y;

  x=5;
  y=0;

  printf("before x=%d, y=%d\n",x,y);

  swap_v(x,y);
//  swap_p(&x,&y);    /* ポインタ引数版を使う時はこの行を有効に */
//  swap_r(x,y);      /*     参照引数版を使う時はこの行を有効に */

  printf("after x=%d, y=%d\n",x,y);

  return 0;
}


void swap_v(int x, int y){
  int tmp;

  tmp = y;
  y = x;
  x = tmp;
}

void swap_p(int *x, int *y){
  int tmp;

  tmp = *y;
  *y = *x;
  *x = tmp;
}

void swap_r(int &x, int &y){
  int tmp;

  tmp = y;
  y = x;
  x = tmp;
}

基本的には良くできていましたが、いくつか気になる間違いがありました。

まず値渡し版ですが、「うまくいかない」とあらかじめ言ってしまったせいか、 手を抜いてしまった人が多い様です。
例えば、
void swap_v(int x, int y){
  y = x;
  x = y;
}
これはそもそも「ポインタ引数」や「参照引数」を用いてもうまくいきません。
また、
void swap_v(int x, int y){
  int tmp;

  tmp = y;
  y = x;
  x = tmp;

  return(x,y)
}
なんてのもありました。気持はわかるような気がしますが、これはコンパイルが通りません。
資料にも書いた通り、return 文で返せるのは一つだけです。(構造体やクラスを学べばまた別ですが、それはここでは触れません)

ポインタ引数版にあった間違いはこちら。
void swap_p(int *x, int *y){
  int *z;

  *z = *y;
  *y = *x;
  *x = *z;
}
これは、値を交換する際のテンポラリの変数をポインタで定義しているのですが、 これはなかなか教育的な間違いなので図を用いて解説します。

上図は、swap_p 関数が呼び出され、入れ換えが行われる直前のスタック領域の模式図です。
ポインタ z は自動変数であり、初期化されていないのでどこをさしているか わかりません。
「わからない」と書いたのは、「使用可能なメモリ領域のどこかを指しているかも知れないし、そうでないかもしれない」という意味です。
つまり swap がうまく行われるかどうかは、これまた処理系依存となります。


ためしにコンパイルして実行してみたところ、Windows+BCC では swap はうまくいかず、PC-Unix+gcc では swap が成功しました。
「成功した」と言っても、上図を見ればわかるように、「たまたまうまくいってるだけ」なので、やはりこのプログラムは間違いです。

繰り返しになりますが、どんな環境でも正しく動作するプログラムを書くよう心がけましょう。

さらに、swap 関数内に printf 文がある人がいました。 やはり printf 文は main 関数にあった方がよいでしょう。
(次回のソートプログラムで、ソート関数から swap 関数を呼び出す際にその理由がわかるかもしれません)


C から入る C++ に戻る