while( *s1 != '\0' ){ if( - - - ) { *s2 = *s1; s2 += 1; } s1 += 1; } *s2 = '\0';
while 文を使って条件付きで文字列のコピーをするとき,最後に終端文字('\0')をコピーすることを忘れている.
誤り例 while( *s++ != '\0' ){ if( (*s >= '0') && (*s <= '9') ){ - - -
1回の繰り返しで,必ず1回インクリメントをするならば for 文を使い,条件付きでインクリメントをするならば while 文を使う,というきまりを,個人的に持つことにすると良い.
誤り例 while( *s != '\0' ){ if( (*s++ >= '0') && (*s++ <= '9') ){ - - -
最初の条件(<='0')を満たさない場合,次の条件に処理が移らないので,インクリメントは1回しか実行されないが,最初の条件を満たす場合は次の条件に処理が移り2回目のインクリメントが実行される.誤解が生じるので,この書き方はしてはいけない.
while( *s != '\0' ){ - - - *s++; }
後置インクリメント(x++)の優先順位は間接参照(*)より高いので,課題1,課題2での場合は誤動作にはならないが,意味の無い参照はしないこと.
誤り例 fgets(buff,BUFSIZ,stdin); if( buff[15] != '\n' ){ - - - }
例えば,fgets で10文字しか入力されなかった場合にでも,15文字目を参照することは誤り.
友達と一緒に考えてプログラムを作ることはあると思いますが,考え方の誤りがないのかをもっと考えるようにしましょう.
誤り例 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) である.
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回のレポートは, 「設計書に必要な事項がそろっているか,特にプログラムのモジュール構成図,関数の説明がそろっているか」, 「ソースプログラムが存在するか」,「操作説明がなされているか」,「動作結果が説明されているか」, 「仕様に対する達成の度合が適切に評価されているか,また,不具合な点が明確で以降の修正にとりかかれるようになっているか」という点に注目しました.
第12回のときに,レポートは一旦返却しますが,そのときに,自分でレポートが十分であったかどうかが,わかると思います.
プログラムというものは,自分で書いたものであっても,数週間たつと別の人が書いたものと同じになるものです.
出題が漠然としていて,解答のしようがないと,悩んだかもしれません. 今回の課題では,FREE や HEAD のインデックスの値が何に基づいて決まったのか, という説明を期待していました.
プログラムでは,必ず何らかの情報に基づいて処理をしています. 何が原因で何が変わるのかを注意するように考えましょう.
カーソルリストのプログラムでは,FREEやHEADのインデックスを順序良く使うので,
push や remove の際に余分な変数は使っていません.
どうすれば効率よく変数が使えるのかは大切な問題ですが,
最初は,効率は無視して,意味的にわかりやすいプログラムを書いてみましょう.
授業中に配布した解答に誤りがありました.
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 のメリットは,使用しないメモリが解放されるので,別のプログラムがメモリを有効に利用することができます.たとえば,第9回リスト操作2で,remove アルゴリズムを勉強しましたが,不要になったノードは,free のポインタが指すので,そこの配列の番目は,後から push でデータを格納するときに使うことができました.
free のデメリットは,hash_get でノードが返り値となっていますが,このノードを使い続けるという使い方をする場合,そのノードの信頼性が無くなることです.ノードを 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