■ よくある誤り


第1回課題

事例1-1:文字列の終端文字を入れていない.

while( *s1 != '\0' ){
  if( - - - ) {
    *s2 = *s1;
    s2 += 1;
  }
  s1 += 1;
}
 *s2 = '\0'; 

while 文を使って条件付きで文字列のコピーをするとき,最後に終端文字('\0')をコピーすることを忘れている.


第2回課題

事例2-1:繰り返し条件に,インクリメント演算子を誤って入れている.

誤り例
while( *s++ != '\0' ){
  if( (*s >= '0') && (*s <= '9') ){
    - - -

1回の繰り返しで,必ず1回インクリメントをするならば for 文を使い,条件付きでインクリメントをするならば while 文を使う,というきまりを,個人的に持つことにすると良い.

事例2-2:1行内で,1つの変数へにインクリメントを2回使っている.

誤り例
while( *s != '\0' ){
  if( (*s++ >= '0') && (*s++ <= '9') ){
    - - -

最初の条件(<='0')を満たさない場合,次の条件に処理が移らないので,インクリメントは1回しか実行されないが,最初の条件を満たす場合は次の条件に処理が移り2回目のインクリメントが実行される.誤解が生じるので,この書き方はしてはいけない.

事例2-3:ポインタのインクリメントを意図していながら,ポインタの間接参照記号を付けている.

while( *s != '\0' ){
  - - -
  *s++;
}

後置インクリメント(x++)の優先順位は間接参照(*)より高いので,課題1,課題2での場合は誤動作にはならないが,意味の無い参照はしないこと.


第3回課題

文字列の終端文字を越えてアクセスしている.

 誤り例 

fgets(buff,BUFSIZ,stdin);
if( buff[15] != '\n' ){
 - - - 
}

例えば,fgets で10文字しか入力されなかった場合にでも,15文字目を参照することは誤り.

課題についての考え方の誤りに次のものがあった.

友達と一緒に考えてプログラムを作ることはあると思いますが,考え方の誤りがないのかをもっと考えるようにしましょう.


第4回課題

画像の最下行の座標計算を誤っている.

 誤り例 
buff[x + sizex * (sizey - y)] = buff[x + sizex * y]
 正解例 
buff[x + sizex * (sizey - 1 - y)] = buff[x + sizex * y]

画像のサイズが 400 x 400 の場合,座標は,(0,0) から (399,399) である.


第5回課題

リスト構造の先頭のノードの引き渡しを誤っている.

Node *list_search_by_range(Node *head,int min, int max)
{
  Node *p;

  - - -
  p = head->next
  - - -
}

 誤り例 
main()
{
  - - -
  p = head.next
  - - -
  p = list_search_by_range(p,min,max)
  - - -
}

 正解例 
main()
{
  - - -
  p = &head
  - - -
  p = list_search_by_range(p,min,max)
  - - -
}

リスト構造を扱う習慣で,最初のノードはダミーノード(データを格納しないノード)である.リスト操作のライブラリを作成する際も,この使い方を踏襲しなくてはならない.

よって,list_search_by_range 関数は,上記のように p = head->next として処理を始めることを基準にするので,main 関数においては,p = &head としなければならない.

(別解) ちなみに,倉持君の解答は,コンパクトです.

main()
  - - -
  p = &head;
  while( (p = list_search_by_range(p, min, max)) != NULL ){
    list_print_node(p);
  }
  - - -

第8回課題

どうやって採点したの?

第8回のレポートは, 「設計書に必要な事項がそろっているか,特にプログラムのモジュール構成図,関数の説明がそろっているか」, 「ソースプログラムが存在するか」,「操作説明がなされているか」,「動作結果が説明されているか」, 「仕様に対する達成の度合が適切に評価されているか,また,不具合な点が明確で以降の修正にとりかかれるようになっているか」という点に注目しました.

第12回のときに,レポートは一旦返却しますが,そのときに,自分でレポートが十分であったかどうかが,わかると思います.

プログラムというものは,自分で書いたものであっても,数週間たつと別の人が書いたものと同じになるものです.


第9回課題

カーソルリストの構造の変化を説明するとは.

出題が漠然としていて,解答のしようがないと,悩んだかもしれません. 今回の課題では,FREE や HEAD のインデックスの値が何に基づいて決まったのか, という説明を期待していました.

プログラムでは,必ず何らかの情報に基づいて処理をしています. 何が原因で何が変わるのかを注意するように考えましょう.

カーソルリストのプログラムでは,FREEやHEADのインデックスを順序良く使うので, push や remove の際に余分な変数は使っていません. どうすれば効率よく変数が使えるのかは大切な問題ですが, 最初は,効率は無視して,意味的にわかりやすいプログラムを書いてみましょう.


第10回課題

heads のダミーノードが初期化されていない

授業中に配布した解答に誤りがありました.

hash_new は以下の赤字部分を追加してください.

Hash *hash_new(int size)
{
  Hash *ans;
  int i;

  ans = (Hash *)malloc(sizeof(Hash));
  if( ans == NULL ){
    fprintf(stderr,"Hash の作成に失敗しました\n");
    exit(-1);
  }
  ans->heads = (Node *)malloc(sizeof(Node)*size);
  if( ans->heads == NULL ){
    fprintf(stderr,"Hash テーブルの作成に失敗しました\n");
    exit(-1);
  }

  for( i = 0; i < size; i++ ){
    ans->heads[i].next = NULL;
  }
  ans->size = size;
  return ans;
}

心得として,malloc で確保した後は初期値を代入する習慣を身につけましょう.

という事は,list.c においても安全のため,list_new_node の際には NULL を入れよう.

strncmp の文字数はいくつを指定するのか.

strncmp の第3引数に,最大何文字まで比較するのかを,指定することができます.今回はキーの文字列の最大値が KEYSIZE ですので,KEYSIZE を指定してください.

「2」や「3」という無意味な数を指定した回答がありましたが,それは誤りです.

「strlen(key) - 1」という回答がありましたが,最後の1文字が比較されないので誤りです.「strlen(key)」ならば正解ですが,長さを数えることが無駄です.

また,引数は文字列を与えるので,strncmp(*key,…) ではなく,strncmp(key,…)になります.

free は必要か?

今回の free は必須ではありません.

線型リストのライブラリを作成する者は使用する条件を設計書に明記しておくこと,ライブラリを使用する者はその条件を確認し,不明な場合は安全な方向を意識して使うこと,の両方が必要です.

free のメリットは,使用しないメモリが解放されるので,別のプログラムがメモリを有効に利用することができます.たとえば,第9回リスト操作2で,remove アルゴリズムを勉強しましたが,不要になったノードは,free のポインタが指すので,そこの配列の番目は,後から push でデータを格納するときに使うことができました.

free のデメリットは,hash_get でノードが返り値となっていますが,このノードを使い続けるという使い方をする場合,そのノードの信頼性が無くなることです.ノードを free すると,ノードに割り当てていたメモリ上に別のルーチンが書き込みをするかもしれないからです.

誤った free の使い方

以下に誤り例を示します.

q = head;
p = head -> next;
while( p != NULL ){
  if( strncmp( p->key, key, KEYSIZE ) == 0 ){
    q->next = p->next;
    free(p);
  }
  q = p;
  p = p-> next;
}

このプログラムでは,free を実行した後,すぐにサブルーチンを終了(return)していないだけでなく,free をした後でも,その領域を参照しています.free をしたポインタは参照してはいけません.


2004.6.24 by tokuhisa