# 2017q3 Homework2 (prefix-search) contributed by <`yuan922`> ### Reviewed by `ZixinYang` - 建議修改程式碼的時候, 不要只是寫把 tst_free_all(root) 改成 tst_free(root), 而是去解釋該 function 的實作差異。 - 記得按照規定去 fork GitHub repository, 並 push 修改後的程式碼。 # 開發環境 ```shell Architecture: x86_64 CPU 作業模式: 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 每核心執行緒數:1 每通訊端核心數:4 Socket(s): 1 NUMA 節點: 1 供應商識別號: GenuineIntel CPU 家族: 6 型號: 60 Model name: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz 製程: 3 CPU MHz: 3390.435 CPU max MHz: 3700.0000 CPU min MHz: 800.0000 BogoMIPS: 6584.89 虛擬: VT-x L1d 快取: 32K L1i 快取: 32K L2 快取: 256K L3 快取: 6144K NUMA node0 CPU(s): 0-3 ``` # 執行程式 >中英文字間請以空白隔開 >[name=課程助教][color=red] 首先嘗試執行 **test_cpy.c** 檔 ``` choice: f find word in tree: Taiwan found Taiwan in 0.000001 sec. ``` 搜尋 **Taiwan** 正常執行 ``` choice: f find word in tree: Taipei Taipei not found. ``` 搜尋 **Taipei** 卻找不到 用其他指令找看看原因: ``` choice: s find words matching prefix (at least 1 char): Taip Taip - searched prefix in 0.000014 sec suggest[0] : Taipa, suggest[1] : Taipalsaari, suggest[2] : Taipei, suggest[3] : Taiping, suggest[4] : Taipu, ``` 用 **s** 搜尋之後發現不是找不到,而是城市名稱接著逗號再接國家名稱 而城市的名稱會包含中間區隔的逗號,所以要城市名稱加上逗號才能用 **f** 搜到 ``` choice: f find word in tree: Taipei, found Taipei, in 0.000001 sec. ``` ```c while ((rtn = fscanf(fp, "%s", word)) != EOF) { char *p = word; if (!tst_ins_del(&root, &p, INS, CPY)) { fprintf(stderr, "error: memory exhausted, tst_insert.\n"); fclose(fp); return 1; } idx++; } ``` 參考 [Lihikoka](https://hackmd.io/AwEwjCDGAsIOwFo4DNLIdSAmaCCcAzAIaQICsARgBxHBlnIBsRBkQA==?view) 同學的共筆之後得知,要修正這問題只要把word最後一個字移除就好。 在while裡面加上: ```c int len = strlen(word); word[len - 1] = 0; ``` 執行**f** ``` choice: f find word in tree: Taipei found Taipei in 0.000001 sec. ``` 結果換成**Taiwan**找不到 ``` choice: f find word in tree: Taiwan Taiwan not found. ``` 參考[stackoverflow](https://l.facebook.com/l.php?u=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F33290218%2Fhow-to-remove-commas-from-a-string-in-c&h=ATN4YEbEvDahTGFHgOsL-4b3E_eF7hGkJMnz909X1yP80B9xddvsAdn2VYoMbVV-cH-bEvoUOZtQ3oj9cIWBLKNAgROo1js_fHQupMJc-PYT0EZWLH455p8pBykPAd0xJUWoq0sC6noWLQqEtZeY8A)上面的寫法: ```c while ((rtn = fscanf(fp, "%s", word)) != EOF) { char *p = word; char *w; char *r; for(w=r=p;*r;r++){ if(*r!=','){ *w++ = *r; } } *w='\0'; ``` 直接檢查字串有無含**逗號**。 ``` choice: f find word in tree: Taipei found Taipei in 0.000001 sec. ``` ``` choice: f find word in tree: Taiwan found Taiwan in 0.000001 sec. ``` 執行結果皆正常。 # FIXME 先檢查 ==test_ref.c== 檔,看到第一個 **FIXME** ```c if (!tst_ins_del(&root, &p, INS, CPY)) ``` 先把 **CPY** 改成 **REF** ```c if (!tst_ins_del(&root, &p, INS, REF)) ``` 但這樣在執行上會有幾個錯誤 * 指令為 **s** : ```shell choice: s find words matching prefix (at least 1 char): New New - searched prefix in 0.000222 sec suggest[0] : New suggest[1] : New suggest[2] : New suggest[3] : New suggest[4] : New ... ``` 可以發現,都會對應到同樣的字 觀察 **test_ref.c** ```c enum { INS, DEL, WRDMAX = 256, STKMAX = 512, LMAX = 1024 }; #define REF INS #define CPY DEL ``` enum的賦值順序為0,1,2...(如果沒事先給定) 所以 **INS=REF=0** , **DEL=CPY=1**, 再看到 **tst.c** ```c if (*p++ == 0) { if (cpy) { /* allocate storage for 's' */ const char *eqdata = strdup(*s); if (!eqdata) return NULL; curr->eqkid = (tst_node *) eqdata; return (void *) eqdata; } else { /* save pointer to 's' (allocated elsewhere) */ curr->eqkid = (tst_node *) *s; return (void *) *s; } } pcurr = &(curr->eqkid); } ``` 將**CPY** 改成 **REF** 之後會直接將 ***s** 指派給 **curr->eqkid**,而***s**又會對應到**word**,因此指標會一直指向同一個起始位址,造成錯誤。 參考[st9007a](https://hackmd.io/CYIwxgnATAhgZlAtAUwCwhI1AOA7GRGMANgAYtSBWPXZARjmMoiA)同學的作法 使用 **strdup()** 修正 ```c char *p = strdup(word); ``` 顯示正常 ```shell suggest[30] : Taiwala suggest[31] : Taiwan suggest[32] : Taixing suggest[33] : Taiynsha suggest[34] : Taiyuan suggest[35] : Taizhou ``` * 指令為 **q** : ```shell choice: q *** Error in `./test_ref': free(): invalid pointer: 0x00007ffe767782d0 *** ``` 指標指向無效的位址。 ```c case 'q': tst_free_all(root); ``` 這裡要更正為 ```c case 'q': tst_free(root); ``` 因為tst_free是用來釋放外部資料。 執行: ```shell choice: q yuan@yuan-OptiPlex-7020:~/prefix-search$ ```