StevenChou43
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- tags: linux2022 --- # 2022q1 Homework2 (quiz2) contributed by < `StevenChou499` > * [作業需求](https://hackmd.io/@sysprog/BybKYf5xc) * [測驗題目](https://hackmd.io/@sysprog/linux2022-quiz2#2022q1-%E7%AC%AC-2-%E9%80%B1%E6%B8%AC%E9%A9%97%E9%A1%8C) --- ## 測驗 `1` > 考慮以下對二個無號整數取平均值的程式碼: > ```c= > #include <stdint.h> > uint32_t average(uint32_t a, uint32_t b) > { > return (a >> 1) + (b >> 1) + (EXP1); > } > ``` > 我們再次改寫為以下等價的實作: > ```c= > uint32_t average(uint32_t a, uint32_t b) > { > return (EXP2) + ((EXP3) >> 1); > } > ``` ### 思考與想法 題目中的第一題因為想到要做平均,所以應該要用 `>>` 來做除以二的動作。但是因為再二進位制中,若兩數的同一位皆為 `1` 則需要進位,而只有 `&` 可以提取出兩數同時出現的 `1` ,因此 `EXP1` 應該是 `a & b` 才對。 而下一題的在再次改寫,因為後面有一個 `>> 1` ,應該是除以二的結果,而如果 `a` 與 `b` 皆需要除以二,應該以 `^` 的方式提取兩者各單獨擁有的位元,再使用 `&` 來找出兩者共同擁有的位元,而因為\兩者皆擁有此位元,所以不需要進行除以二的動作。所以 `EXP2` 為 `a & b` ,且 `EXP3` 為 `a ^ b` 。 :::success EXP1 : a & b EXP2 : a & b EXP3 : a ^ b ::: ### 延伸問題 > 比較上述實作在編譯器最佳化開啟的狀況,對應的組合語言輸出,並嘗試解讀 (可研讀 CS:APP 第 3 章) > 研讀 Linux 核心原始程式碼 include/linux/average.h,探討其 Exponentially weighted moving average (EWMA) 實作,以及在 Linux 核心的應用 ## 測驗 `2` > 改寫〈解讀計算機編碼〉一文的「不需要分支的設計」一節提供的程式碼 min,我們得到以下實作 (max): > ```c= > #include <stdint.h> > uint32_t max(uint32_t a, uint32_t b) > { > return a ^ ((EXP4) & -(EXP5)); > } > ``` ### 思考與想法 因為答案需要回傳 `a` 或是 `b` ,所以首先想到的是若要回傳 `a` 則需要利用 `^` 的特性,也就是任何數值在與 `0` 做 `EXR` 後,還會是自己本身。因此我們可以知道 `((EXP4) & -(EXP5))` 等於 `0` 。而若是需要回傳 `b` ,則可以想到 `b` 在與 `a` 做兩次 `^` 後又會回到 `b` ,因此若需要回傳 `b` ,則 `((EXP4) & -(EXP5))` 等於 `a ^ b` ,以下為兩種結果的表格: | 情況 | a > b (回傳a) | a < b (回傳b) | |:------------------:|:-------------:|:-------------:| | ((EXP4) & -(EXP5)) | 0 | a ^ b | --- 而因為需要回傳 `0` 或是 `a ^ b` 且中間還需要做 `&` 的操作,因此 `(EXP4)` 應該要等於 `a ^ b` ,且 `-(EXP5)` 在 `a > b` 時應該要等於 `0x0` ,在 `a < b` 時應該要等於 `0xFFFFFFFF` ,也就是 `-1` 。依據這樣的條件,在加上去除加上負號後的轉換, `(EXP5)` 應該要是 `0` 或 `1` ,因此 `(EXP5)` 應該為 `(a < b)` ,此時若 `a > b` ,會回傳 `0` ,加上負號後為 `0` ,與 `a ^ b` 做 `^` 後為 `0` ,而 `a ^ 0` 等於 `a` ,就會回傳 `a` 。若 `a < b` ,會回傳 `1` ,加上負號後為 `0xFFFFFFFF` ,與 `a ^ b` 做 `^` 後為 `a ^ b` ,最後則會回傳 `b`,以下為詳細講述計算過程的表格: | 情況 | a > b (回傳a) | a < b (回傳b) | |:--------------------------:|:-------------:|:-------------:| | a < b | 0 | 1 | | -(a < b) | 0x0 | 0xFFFFFFFF | | a ^ b | a ^ b | a ^ b | | (a ^ b) & -(a < b) | 0 | a ^ b | | **a ^ (a ^ b) & -(a < b)** | **a** | **b** | 因此我們可以得知 `EXP4` 等於 `a ^ b` , `EXP5` 等於 `a < b` 。 :::success EXP4 : a ^ b EXP5 : a < b ::: ### 延伸問題 ## 測驗 `3` > 考慮以下 64 位元 GCD (greatest common divisor, 最大公因數) 求值函式: > ```c= > #include <stdint.h> > uint64_t gcd64(uint64_t u, uint64_t v) > { > if (!u || !v) return u | v; > int shift; > for (shift = 0; !((u | v) & 1); shift++) { > u /= 2, v /= 2; > } > while (!(u & 1)) > u /= 2; > do { > while (!(v & 1)) > v /= 2; > if (u < v) { > v -= u; > } else { > uint64_t t = u - v; > u = v; > v = t; > } > } while (COND); > return RET; > } > ``` ### 思考與想法 因為是要求兩數之最大公因數,因此前面的程式碼: ```c=6 for (shift = 0; !((u | v) & 1); shift++) { u /= 2, v /= 2; } ``` 此階段為利用 `shift` 紀錄下總共有幾次方的 `2` 兩數可以進行整除, ```c=9 while (!(u & 1)) u /= 2; do { while (!(v & 1)) v /= 2; ``` 接著下方的程式碼則是將剩下無法成為公因數的 `2` 去除。 而最後一段的 `do while` 迴圈則可以看出是用來計算最大公因數的[輾轉相除法](https://zh.wikipedia.org/zh-tw/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95)。而[輾轉相除法](https://zh.wikipedia.org/zh-tw/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95)的結束條件為兩數字相等也就是兩者相減為 `0` ,且因為當最後兩數相等時, `do while` 迴圈中會進入 `else` 的流程,此時 `v` 會等於 `0` ,因此 `while` 結束的條件為 `v == 0` ,所以 `COND` 為 `v` ,當 `v` 不為 `0` 時代表還需要進行下一次的[輾轉相除法](https://zh.wikipedia.org/zh-tw/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95),直到 `v` 等於 `0` 為止。而因為最後要回傳兩者之最大公因數,此時之 `u` 為去除二倍數之最大公因數,所以最後還需要再利用一開始求出的 `shift` 乘回算出來的 `u` ,因此 `RET` 為 `u << shift` 。 :::success COND : v RET : u << shift ::: ### 延伸問題 ## 測驗 `4` > 改寫的程式碼如下: > ```c= > #include <stddef.h> > size_t improved(uint64_t *bitmap, size_t bitmapsize, uint32_t *out) > { > size_t pos = 0; > uint64_t bitset; > for (size_t k = 0; k < bitmapsize; ++k) { > bitset = bitmap[k]; > while (bitset != 0) { > uint64_t t = EXP6; > int r = __builtin_ctzll(bitset); > out[pos++] = k * 64 + r; > bitset ^= t; > } > } > return pos; > } > ``` > 其中第 9 行的作用是找出目前最低位元的 1,並紀錄到 t 變數。舉例來說,若目前 bitset 為 000101b,那 t 就會變成000001b,接著就可以透過 XOR 把該位的 1 清掉,其他保留 (此為 XOR 的特性)。 ### 思考與想法 因為變數 `t` 是想要將原數字中二進位最低位的 `1` 保留下來,而由二補數系統(two's complement)的定義,一數字在加上 `-` (負號)並與原本的數相加後,會因為一直進位而變成零,而最一開始進位的位置就是該數在二進位制中最低位的 `1` 。因此 `EXP6` 等於 `bitset & -bitset` ,這樣第一次進位的位置即是兩數同時擁有 `1` 的位置。 ```c 6 0110 -6 1010 + ----------- 0 (1)0000 ^ -開始進位的位置(同時都擁有1) ``` | 6 | -6 | 6 & -6 | |:----:|:----:|:------:| | 0110 | 1010 | 0010 | :::success EXP6 : bitset & -bitset ::: ### 延伸問題 ## 測驗 `5` > 以下是 LeetCode 166. Fraction to Recurring Decimal 的可能實作: > ```c= > #include <stdbool.h> > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > #include "list.h" > > struct rem_node { > int key; > int index; > struct list_head link; > }; > > static int find(struct list_head *heads, int size, int key) > { > struct rem_node *node; > int hash = key % size; > list_for_each_entry (node, &heads[hash], link) { > if (key == node->key) > return node->index; > } > return -1; > } > > char *fractionToDecimal(int numerator, int denominator) > { > int size = 1024; > char *result = malloc(size); > char *p = result; > > if (denominator == 0) { > result[0] = '\0'; > return result; > } > > if (numerator == 0) { > result[0] = '0'; > result[1] = '\0'; > return result; > } > > /* using long long type make sure there has no integer overflow */ > long long n = numerator; > long long d = denominator; > > /* deal with negtive cases */ > if (n < 0) > n = -n; > if (d < 0) > d = -d; > > bool sign = (float) numerator / denominator >= 0; > if (!sign) > *p++ = '-'; > > long long remainder = n % d; > long long division = n / d; > > sprintf(p, "%ld", division > 0 ? (long) division : (long) -division); > if (remainder == 0) > return result; > > p = result + strlen(result); > *p++ = '.'; > > /* Using a map to record all of reminders and their position. > * if the reminder appeared before, which means the repeated loop begin, > */ > char *decimal = malloc(size); > memset(decimal, 0, size); > char *q = decimal; > > size = 1333; > struct list_head *heads = malloc(size * sizeof(*heads)); > for (int i = 0; i < size; i++) > INIT_LIST_HEAD(&heads[i]); > > for (int i = 0; remainder; i++) { > int pos = find(heads, size, remainder); > if (pos >= 0) { > while (PPP > 0) > *p++ = *decimal++; > *p++ = '('; > while (*decimal != '\0') > *p++ = *decimal++; > *p++ = ')'; > *p = '\0'; > return result; > } > struct rem_node *node = malloc(sizeof(*node)); > node->key = remainder; > node->index = i; > > MMM(&node->link, EEE); > > *q++ = (remainder * 10) / d + '0'; > remainder = (remainder * 10) % d; > } > > strcpy(p, decimal); > return result; > } > ``` ### 思考與想法 程式碼講解: 因為其程式碼有一定的長度,因此需要經過 `trace code` 再做完整的判斷會比較好。 先從程式開始執行的第24行開始: ```c=24 char *fractionToDecimal(int numerator, int denominator) { int size = 1024; char *result = malloc(size); char *p = result; if (denominator == 0) { result[0] = '\0'; return result; } if (numerator == 0) { result[0] = '0'; result[1] = '\0'; return result; } /* using long long type make sure there has no integer overflow */ long long n = numerator; long long d = denominator; /* deal with negtive cases */ if (n < 0) n = -n; if (d < 0) d = -d; ``` 此段是在配置足夠的記憶體來存取在做運算後的小數點,並先將分母以及分子為零的狀況排除。 接著是第55行: ```c=55 long long remainder = n % d; long long division = n / d; sprintf(p, "%ld", division > 0 ? (long) division : (long) -division); if (remainder == 0) return result; ``` 此段主要是先計算出該數的商數與餘數,接著利用 `sprintf` 將 `division` 存入 `%ld` 再轉成 `char` 存入 `p` 。 第62行: ```c=62 p = result + strlen(result); *p++ = '.'; /* Using a map to record all of reminders and their position. * if the reminder appeared before, which means the repeated loop begin, */ char *decimal = malloc(size); memset(decimal, 0, size); char *q = decimal; ``` 此段是將小數點加入 `p` ,再定義一個存放小數的變數 `decimal` ,配置空間後再用 `memset` 轉換其全部內容成 `0` 。 第72行: ```c=72 size = 1333; struct list_head *heads = malloc(size * sizeof(*heads)); for (int i = 0; i < size; i++) INIT_LIST_HEAD(&heads[i]); ``` 此段開始要真正做小數運算的動作,先重新定義 `size` 為 `1333` ,接著定義一個 `heads` 並配置記憶體,在利用 `INIT_LIST_HEAD` ,依序進行初始化。 第77行: ```c=77 for (int i = 0; remainder; i++) { int pos = find(heads, size, remainder); if (pos >= 0) { while (PPP > 0) *p++ = *decimal++; *p++ = '('; while (*decimal != '\0') *p++ = *decimal++; *p++ = ')'; *p = '\0'; return result; } ``` 接著利用 `for` 迴圈開始進行計算,透過呼叫 `find` 函式找出是否有相同的 `key` ,若有則回傳他的 `index` ,若無則回傳 `-1` 。 * `find` 函式: ```c=13 static int find(struct list_head *heads, int size, int key) { struct rem_node *node; int hash = key % size; list_for_each_entry (node, &heads[hash], link) { if (key == node->key) return node->index; } return -1; } ``` 而獲得 `pos` 後,變可以印出小數點的數值,並回傳 `result` 。 第89行: ```c=89 struct rem_node *node = malloc(sizeof(*node)); node->key = remainder; node->index = i; MMM(&node->link, EEE); *q++ = (remainder * 10) / d + '0'; remainder = (remainder * 10) % d; ``` 若沒有找到相同的 `key` ,則需要將其 `remainder` 與 `i` 記錄下來,並將其存入配置圍成的 `struct list_head*` 中,接著最後兩行是存入當前餘數 `remainder` 的數值至 `q` 與更新 `remainder` 的數值。此處的 `+ '0'` ,其實為加上 `48` ,將餘數算出後型態從 `long long` 轉換至 `char` 。 以下為解釋(以 `remainder = 3, d = 6` 為例): ```c (remainder * 10) / d + '0'; 3 * 10 / 6 + '0'; 30 / 6 + '0'; 5 + '0'; 5 + 48; 53 = '5'; // 利用 + '0' 將int轉換成char ``` 0~9 之ASCII編碼: | Binary | Decimal | Character | |:-------:|:-------:|:---------:| | 0110000 | 48 | 0 | | 0110001 | 49 | 1 | | 0110010 | 50 | 2 | | 0110011 | 51 | 3 | | 0110100 | 52 | 4 | | 0110101 | 53 | 5 | | 0110110 | 54 | 6 | | 0110111 | 55 | 7 | | 0111000 | 56 | 8 | | 0111001 | 57 | 9 | 由上述的運作情況,我們可以知道 `PPP` 為 `pos--` ,因為 `pos` 為記住小數點後不重複的位數,因此當 `pos` 等於零時,代表不重複的小數已經計算完了。而在後面的 `MMM` 與 `EEE` 則分別是 `list_add` 與 `&heads[remainder % size]` 。此處的用意是將餘數與位置放入 `struct list_head` 的結構 `node*` 當中,再利用 `list_add` 加入 `head[remainder % size]` 中。 整個運作上其實為一個雜湊表,透過雜湊表找出循環與非循環的小數。 以下為實際運作的示意圖(以分母為 `90` ,分子為 `102` 為例): * 找出第一次運算下的商數以及餘數 (102 / 90 = 1 ... 12) ```c long long n = numerator; // n = 102 long long d = denominator; // d = 90 long long remainder = n % d; // remainder = 12 long long division = n / d; // division = 1 ``` * 將商數存入 `result` 並同時加入小數點 ```c sprintf(p, "%ld", division > 0 ? (long) division : (long) -division); // p = result = 1 if (remainder == 0) return result; p = result + strlen(result); *p++ = '.'; // result = 1. ^ ^ result的位置 p的位置 ``` * 初始化 `decimal` 並準備存入小數 ```c char *decimal = malloc(size); memset(decimal, 0, size); char *q = decimal; // q = decimal = '000000000000000...' ``` * 建立一雜湊表並初始化 ```c size = 1333; struct list_head *heads = malloc(size * sizeof(*heads)); for (int i = 0; i < size; i++) INIT_LIST_HEAD(&heads[i]); ``` ```graphviz digraph INIT { rankdir = "LR" subgraph h1332{ heads1332[label=" {<p1332>prev|<f1332>heads[1332]|<n1332>next}", shape=record, fixedsize=false,width=3] heads1332:p1332 -> heads1332:f1332 heads1332:n1332 -> heads1332:f1332 } omit[label = ".\n.\n.\n",shape = none] subgraph h2{ heads2[label=" {<p2>prev|<f2>heads[2]|<n2>next}", shape=record, fixedsize=false,width=3] heads2:p2 -> heads2:f2 heads2:n2 -> heads2:f2 } subgraph h1{ heads1[label=" {<p1>prev|<f1>heads[1]|<n1>next}", shape=record, fixedsize=false,width=3] heads1:p1 -> heads1:f1 heads1:n1 -> heads1:f1 } subgraph h0{ heads0[label=" {<p0>prev|<f0>heads[0]|<n0>next}", shape=record, fixedsize=false,width=3] heads0:p0 -> heads0:f0 heads0:n0 -> heads0:f0 } } ``` * 進入第一個 `for` 迴圈 (i = 0): ```c for (int i = 0; remainder; i++) { int pos = find(heads, size, remainder); if (pos >= 0) { while (pos-- > 0) *p++ = *decimal++; *p++ = '('; while (*decimal != '\0') *p++ = *decimal++; *p++ = ')'; *p = '\0'; return result; } struct rem_node *node = malloc(sizeof(*node)); node->key = remainder; node->index = i; list_add(&node->link, &heads[remainder % size]); *q++ = (remainder * 10) / d + '0'; remainder = (remainder * 10) % d; } ``` `pos` 回傳為 `-1` ,引此不會進入 `if` 條件式,並且把 `remainder` 與 `i` 加入 `node*` ,並透過 `list_add` 加入 `heads[remainder % size]` ,即為 `heads[12]` 。 ```graphviz digraph INIT { rankdir = "LR" subgraph h1332{ heads1332[label=" {<p1332>prev|<f1332>heads[1332]|<n1332>next}", shape=record, fixedsize=false,width=3] heads1332:p1332 -> heads1332:f1332 heads1332:n1332 -> heads1332:f1332 } omit1[label = ".\n.\n.\n",shape = none] subgraph h12{ heads12[label=" {<p12>prev|<f12>heads[12]|<n12>next}", shape=record, fixedsize=false,width=3] heads12:p12 -> heads12:f12 } omit[label = ".\n.\n.\n",shape = none] subgraph h0{ heads0[label=" {<p0>prev|<f0>heads[0]|<n0>next}", shape=record, fixedsize=false,width=3] heads0:p0 -> heads0:f0 heads0:n0 -> heads0:f0 } node1[label="{<prev>prev|<node>node|<next>next}|{<index>index = 0|<key>key = 12}", shape = record, width = 1] heads12:n12 -> node1:n } ``` 加入雜湊表後,接著將餘數記住並加入 `q` 中,並且重新計算下一次的餘數 ```c *q++ = (remainder * 10) / d + '0'; // q = 1 remainder = (remainder * 10) % d; // remainder = 30 ``` * 再次進入下一個 `for` 迴圈 (i = 1): 進入 `for` 迴圈後, `pos` 會回傳 `-1` ,接著再次加入 `heads[30]` 中。 ```graphviz digraph INIT { rankdir = "LR" subgraph h1332{ heads1332[label=" {<p1332>prev|<f1332>heads[1332]|<n1332>next}", shape=record, fixedsize=false,width=3] heads1332:p1332 -> heads1332:f1332 heads1332:n1332 -> heads1332:f1332 } omit2[label = ".\n.\n.\n",shape = none] subgraph h30{ heads30[label=" {<p30>prev|<f30>heads[30]|<n30>next}", shape=record, fixedsize=false,width=3] heads30:p30 -> heads30:f30 } omit1[label = ".\n.\n.\n",shape = none] subgraph h12{ heads12[label=" {<p12>prev|<f12>heads[12]|<n12>next}", shape=record, fixedsize=false,width=3] heads12:p12 -> heads12:f12 } omit[label = ".\n.\n.\n",shape = none] subgraph h0{ heads0[label=" {<p0>prev|<f0>heads[0]|<n0>next}", shape=record, fixedsize=false,width=3] heads0:p0 -> heads0:f0 heads0:n0 -> heads0:f0 } node1[label="{<prev>prev|<node>node|<next>next}|{<index>index = 0|<key>key = 12}", shape = record, width = 1] node2[label="{<prev>prev|<node>node|<next>next}|{<index>index = 1|<key>key = 30}", shape = record, width = 1] heads12:n12 -> node1:n heads30:n30 -> node2:n } ``` 接著再次紀錄商數至 `q` ,並刷新餘數。 ```c *q++ = (remainder * 10) / d + '0'; // q = 1.3 remainder = (remainder * 10) % d; // remainder = 30 ``` * 第三次進入 `for` 迴圈(i = 2): 進入 `for` 迴圈後, `find` 函式會進入 `heads[30]` 尋找相同的 `key` ,找到相同的 `key` 後,便會回傳 `1` 給 `pos` 。接著進入 `if` 判斷式,將 `decimal` 的內容複製給 `p` ,因為 `pos` 等於 `1` ,所以只會將 `decimal` 的第一位的 `1` 複製給 `p` ,此時 `result` 為 `1.1` 。加上 `(` 並複製剩下的內容直到遇到 `\0` ,最後在加上 `)\0` 即可回傳。 ```c int pos = find(heads, size, remainder); // pos = 1 if (pos >= 0) { while (PPP > 0) *p++ = *decimal++; // result = 1.1 *p++ = '('; // result = 1.1( while (*decimal != '\0') *p++ = *decimal++; // result = 1.1(3 *p++ = ')'; // result = 1.1(3) *p = '\0'; // result = 1.1(3)\0 return result; ``` :::success PPP : pos-- MMM : list_add EEE : &heads[remainder % size] ::: ## 測驗 `6` > \_\_alignof\_\_ 是 GNU extension,以下是其可能的實作方式: > ```c > /* > * ALIGNOF - get the alignment of a type > * @t: the type to test > * > * This returns a safe alignment for the given type. > */ > #define ALIGNOF(t) \ > ((char *)(&((struct { char c; t _h; } *)0)->M) - (char *)X) > ``` ### 思考與想法 其程式碼講解: 先定義一個結構體其中擁有 `char c` 與 `t _h` 。 ```c struct { char c; t _h; } ``` 再將其結構之起始位置設定為 `0` 。 ```c (struct { char c; t _h; } *)0 ``` 接著找出其結構中元素 `_h` 的位置。 ```c ((char *)(&((struct { char c; t _h; } *)0)->M) ``` 最後與 `X` 的位置相減。 ```c ((char *)(&((struct { char c; t _h; } *)0)->M) - (char *)X) ``` 因為 `__alignof__` 就是計算 `type t` 的長度,所以 `M` 就是需要求的 `_h` ,而 `X` 就是 `0` 。 以 `_h` 是 `int` 為例: ```c #include <stdio.h> #define ALIGNOF(t) \ ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) int main(void) { printf("%ld\n", ALIGNOF(int)); return 0; } ``` 其結構實際樣貌: ```graphviz digraph INIT { rankdir = "LR" subgraph mem{ memforint[label=" {<m0>char|<m1>|<m2>|<m3>|<m4>int|<m5>int|<m6>int|<m7>int}", shape=record, fixedsize=fasle, width=5] } } ``` ```c 0:定義結構的起始位置為0 | v | | | | | | | | | {char} { int } ^ ^ | | 0:char的起始位置 4:int的起始位置 ``` 其執行結果: ![](https://i.imgur.com/B87jdHU.png) :::success M : _h X : 0 ::: ## 測驗 `7` > 考慮貌似簡單卻蘊含實作深度的 FizzBuzz 題目: > * 從 1 數到 n,如果是 3的倍數,印出 “Fizz” > * 如果是 5 的倍數,印出 “Buzz” > * 如果是 15 的倍數,印出 “FizzBuzz” > * 如果都不是,就印出數字本身 > > 以下是利用 bitwise 和上述技巧實作的 FizzBuzz 程式碼: ( `bitwise.c` ) > ```c > static inline bool is_divisible(uint32_t n, uint64_t M) > { > return n * M <= M - 1; > } > > static uint64_t M3 = UINT64_C(0xFFFFFFFFFFFFFFFF) / 3 + 1; > static uint64_t M5 = UINT64_C(0xFFFFFFFFFFFFFFFF) / 5 + 1; > > int main(int argc, char **argv) > { > for (size_t i = 1; i <= 100; i++) { > uint8_t div3 = is_divisible(i, M3); > uint8_t div5 = is_divisible(i, M5); > unsigned int length = (2 << KK1) << KK2; > > char fmt[9]; > strncpy(fmt, &"FizzBuzz%u"[(9 >> div5) >> (KK3)], length); > fmt[length] = '\0'; > > printf(fmt, i); > printf("\n"); > } > return 0; > } > ``` ### 思考與想法 因為題目要求依據各數字是否為 `3` 或 `5` 的倍數去做變化,而若只是 `3` 或是 `5` 的倍數只需要印出 `Fizz` 或是 `Buzz` ,所以我們可以知道 `length` 在皆否的情況下為 `2` ,只為其中一數之倍數為 `4` ,同時為兩數之倍數的話為 `8` ,而剛好 `2 << 1` 等於 `4` 且 `2 << 2` 等於 `8` 。因此我們可以知道 `KK1` 與 `KK2` 分別為 `div3` 和 `div5` ,也可以互換,因為不管先後都會作到 `<<` 的動作。 | 情況 | `3` 的倍數 | `5` 的倍數 | `15` 的倍數 | 皆不是 | |:---------------:|:----------:|:----------:|:-----------:|:------:| | `length` 的數值 | 4 | 4 | 8 | 2 | 搞定 `length` 的問題後,後面的 `strncpy` 則是依據不同情況去複製不同位置以及大小之字串。若是 `3` 或是同時是 `3` 與 `5` 的倍數,則要從 `0` 的位置開始複製,若是單純 `5` 的倍數,要從 `4` 的位置開始複製,而若兩數皆不是的話則要從 `8` 的位置開始複製。 | 情況 | `3` 的倍數 | `5` 的倍數 | 同時為 `3` 與 `5` 的倍數 | 皆不是 | |:--------------:|:----------:|:----------:|:------------------------:|:------:| | 開始複製之位置 | 0 | 4 | 0 | 8 | ```c strncpy(fmt, &"FizzBuzz%u"[(9 >> div5) >> (KK3)], length); ``` 而因為 `div5` 已經決定好了,所以若是單純為 `5` 的倍數, `9 >> 5` 等於 `4` ,剛好是開始複製的位置,此時 `KK3` 應該要為 `0` 。而若是 `3` 的倍數或是 `15` 的倍數,則需要一直位移直到數值為 `0` ,便可以從起始位置開始複製,所以 `KK3` 應該是 `div3 << 2` ,這樣在單純只為 `3` 的倍數的情況下,也可以讓 `9` 位移 `4` 次,變成 `0` 的結果。 但是當兩數皆不為倍數時,其值為 `9` ,此時只會印出 `u` 的結果,與 `8` 的結果不符,因此 `9` 應該改為 `8` ,這樣不會影響原本推導的結果,又可以讓兩數皆不為倍數的情況下複製並輸出正確的結果。 ```c strncpy(fmt, &"FizzBuzz%u"[(8 >> div5) >> (KK3)], length); ``` :::success KK1 : div3 KK2 : div5 KK3 : div3 << 2 :::

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully