{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Week15.ipynb","provenance":[],"collapsed_sections":[],"authorship_tag":"ABX9TyPxz2YmztIseVFqf2HDJmfj"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["#第十五週上課內容"],"metadata":{"id":"9xoK4OIjIfZA"}},{"cell_type":"markdown","source":["###GitHub 教材參考資料\n","\n","[https://github.com/htchen/i2p-nthu/tree/master/程式設計一/更多關於指標的用法](https://github.com/htchen/i2p-nthu/tree/master/%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88%E4%B8%80/Supplementary%20Material%201)\n","\n"],"metadata":{"id":"05G1Fqg6GRZX"}},{"cell_type":"markdown","source":["## Example 1\n","## qsort\n","[C referecne 對於 qsort 的說明](http://en.cppreference.com/w/c/algorithm/qsort)\n","\n","\n","```C\n","void qsort( void *ptr, size_t count, size_t size, int (*comp)(const void *, const void *) )\n","```\n","\n","*   *ptr*  \n","    指向要排序的陣列的起始位置的指標\n","*   *count*  \n","    在陣列中有幾個元素要排序\n","*   *size*  \n","    在陣列中每個元素的大小，以 byte 為單位\n","*   *comp*  \n","    比較用的函數，回傳正數代表第一個參數比第二個參數小，回傳負數代表第一個參數比第二個參數大，回傳0代表兩個參數相等\n","\n","底下的程式碼示範了如何使用`qsort`"],"metadata":{"id":"3VvMqR5g1CKX"}},{"cell_type":"code","source":["%%writefile E15_01.c\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","int compare_int(const void *a, const void *b)\n","{\n","    const int *va = (const int *) a;\n","    const int *vb = (const int *) b;  \n","    return *va-*vb;\n","}\n","\n","int compare_double(const void *a, const void *b)\n","{\n","    const double *da = (const double *) a;\n","    const double *db = (const double *) b;\n","    return (*db < *da) - (*db > *da);\n","}\n","\n","int main(void)\n","{\n","    int data1[SIZE];\n","    double data2[SIZE];\n","    int i;\n","\n","    for (i=0; i<SIZE; i++) {\n","        data1[i] = rand()%SIZE;\n","        data2[i] = (double)rand()/RAND_MAX;\n","    }\n","\n","    printf(\"original: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%d \", data1[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"  sorted: \");\n","    qsort(data1, SIZE, sizeof(int), compare_int);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%d \", data1[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"original: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%.2f \", data2[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"  sorted: \");\n","    qsort(data2, SIZE, sizeof(double), compare_double);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%.2f \", data2[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    return 0;\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"lS9pUaIN1Pxt","executionInfo":{"status":"ok","timestamp":1653390444992,"user_tz":-480,"elapsed":483,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"5c021981-4020-4b1a-ef31-768b6459c6d4"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E15_01.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc E15_01.c -o E15_01\n","./E15_01"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"9AhJVeEt1xrC","executionInfo":{"status":"ok","timestamp":1653390487913,"user_tz":-480,"elapsed":335,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"0927d013-345b-4e7d-bda7-fd49e64c497f"},"execution_count":5,"outputs":[{"output_type":"stream","name":"stdout","text":["original: 3 7 3 6 9 2 0 3 0 2 \n","  sorted: 0 0 2 2 3 3 3 6 7 9 \n","original: 0.39 0.80 0.20 0.77 0.55 0.63 0.51 0.92 0.72 0.61 \n","  sorted: 0.20 0.39 0.51 0.55 0.61 0.63 0.72 0.77 0.80 0.92 \n"]},{"output_type":"execute_result","data":{"text/plain":[""]},"metadata":{},"execution_count":5}]},{"cell_type":"markdown","source":["## Example 2\n","## 對固定長度的字元陣列排序\n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","int compare(const void *a, const void *b)\n","{\n","    const char *ca = (const char *) a;\n","    const char *cb = (const char *) b;\n","    return strcmp(ca, cb);\n","}\n","\n","int main(void)\n","{\n","    char strs[SIZE][4] = {\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\",\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\"\n","    };\n","    int i;\n","\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", strs[i]);\n","    }\n","    printf(\"\\n\");\n","    qsort(strs, SIZE, 4*sizeof(char), compare);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", strs[i]);\n","    }\n","\n","    return 0;\n","}\n","```\n","\n","上面的方法可以正確運作的原因是字串長度固定而且連續地被放置在記憶體中  \n","`strs[SIZE][4]`這個二維陣列的內容如下表，`strs[0]`對應到`\"aab\"`，`strs[1]`對應到`\"abc\"`  \n","但是其實這樣的二維陣列，在記憶體中仍然是用一維方式循序放置  \n","\n","memory          |   ptr[0]  |   ptr[1]  |   ptr[2]  |   ptr[3]\n","----------------|-----------|-----------|-----------|-----------\n","ptr = strs[0]   |   `'a'`   |   `'a'`   |   `'b'`   |   `'\\0'`    \n","ptr = strs[1]   |   `'a'`   |   `'b'`   |   `'c'`   |   `'\\0'`   \n","ptr = strs[2]   |   `'a'`   |   `'a'`   |   `'a'`   |   `'\\0'`   \n","ptr = strs[3]   |   `'a'`   |   `'b'`   |   `'b'`   |   `'\\0'`   \n","`...`           |   `...`   |   `...`   |   `...`   |   `...`    \n","\n","陣列總共有 10 個元素，每個陣列的元素包含 3 個英文字元外加後面跟著一個`'\\0'`字元 總共 4 bytes  \n","因此我們可以用`qsort(strs, SIZE, 4*sizeof(char), (int (*) (const void *, const void *))strcmp)`  \n","讓`qsort`以陣列元素為基本單位替我們排序，也就是以 4 bytes 為單位進行個別元素的比對與搬動  \n","最後`strs`的內容會直接被修改，並由小排到大  \n","\n","\n"],"metadata":{"id":"Syehx82N2OZp"}},{"cell_type":"code","source":["%%writefile E15_02.c\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","int compare(const void *a, const void *b)\n","{\n","    const char *ca = (const char *) a;\n","    const char *cb = (const char *) b;\n","    return strcmp(ca, cb);\n","}\n","\n","int main(void)\n","{\n","    char strs[SIZE][4] = {\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\",\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\"\n","    };\n","    int i;\n","\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", strs[i]);\n","    }\n","    printf(\"\\n\");\n","    qsort(strs, SIZE, 4*sizeof(char), compare);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", strs[i]);\n","    }\n","\n","    return 0;\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"hz78i_TE7k9u","executionInfo":{"status":"ok","timestamp":1653391278775,"user_tz":-480,"elapsed":280,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"970094fb-c0a9-4b2c-90b2-b045a742815d"},"execution_count":6,"outputs":[{"output_type":"stream","name":"stdout","text":["Writing E15_02.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc E15_02.c -o E15_02\n","./E15_02"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"yorO5mvE7rq3","executionInfo":{"status":"ok","timestamp":1653391284368,"user_tz":-480,"elapsed":307,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"d7137810-760e-4305-f352-01e74d4ffe36"},"execution_count":7,"outputs":[{"output_type":"stream","name":"stdout","text":["aab\n","abc\n","aaa\n","abb\n","acb\n","aab\n","abc\n","aaa\n","abb\n","acb\n","\n","aaa\n","aaa\n","aab\n","aab\n","abb\n","abb\n","abc\n","abc\n","acb\n","acb\n"]},{"output_type":"execute_result","data":{"text/plain":[""]},"metadata":{},"execution_count":7}]},{"cell_type":"markdown","source":["## Example 3\n","\n","## 對非固定長度的字串排序\n","\n","另一種方式，可以透過指標陣列，對不同長度的字串排序\n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","int compare_str_ptr(const void *a, const void *b)\n","{\n","    char **pa;\n","    char **pb;\n","    pa = (char **) a;\n","    pb = (char **) b;\n","    return strcmp(*pa, *pb);\n","}\n","\n","int main(void)\n","{\n","    char strs[SIZE][4] ={\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\",\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\"\n","    };\n","    char *ptrs[SIZE];\n","    int i;\n","\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", strs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","\n","    for (i=0; i<SIZE; i++) {\n","        ptrs[i] = strs[i];\n","    }\n","    qsort(ptrs, SIZE, sizeof(char*), compare_str_ptr);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", ptrs[i]);\n","    }\n","    printf(\"\\n\");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s\\n\", strs[i]);\n","    }\n","\n","\n","    return 0;\n","}\n","```\n","\n","`ptrs`是一個指標陣列  \n","因此`ptrs`的每個元素都是一個指標  都可以用來記錄某個記憶體位置  \n","我們先用`ptrs`的每個元素`ptrs[i]`分別記住每個字串的開始位址  \n","```C\n","for (i=0; i<SIZE; i++) {\n","    ptrs[i] = strs[i];\n","}\n","```\n","\n","接下來對指標陣列進行排序  \n","依照`ptrs`的每個元素所指到的字串用`strcmp`進行比較以後，將`ptrs`的元素搬動  \n","所以只是調換指標的順序 (也就是`ptrs`元素的順序)，實際的資料`strs`不會被更改  \n","\n","請注意這時候`compare`函數的寫法\n","\n","```C\n","int compare_str_ptr(const void *a, const void *b)\n","{\n","    char * *pa;\n","    char * *pb;\n","    pa = (char **) a;\n","    pb = (char **) b;\n","    return strcmp(*pa, *pb);\n","}\n","```\n","\n","*   被搬動的東西是指標\n","*   用來比較則是指標所指到的字串\n","\n","我們可以換個格式顯示陣列內容，執行底下附的程式碼會輸出下表的內容  \n","對照記憶體位址以及`strs`的內容就會看出端倪\n","\n","輸出  \n","\n","```\n","strs: aab0|abc0|aaa0|abb0|acb0|aab0|abc0|aaa0|abb0|acb0|\n","ptrs: 0028FEE4|0028FEE8|0028FEEC|0028FEF0|0028FEF4|0028FEF8|0028FEFC|0028FF00|0028FF04|0028FF08|\n","after sorting\n","ptrs: 0028FF00|0028FEEC|0028FEE4|0028FEF8|0028FEF0|0028FF04|0028FEE8|0028FEFC|0028FF08|0028FEF4|\n","ptrs: aaa0|aaa0|aab0|aab0|abb0|abb0|abc0|abc0|acb0|acb0|\n","strs: aab0|abc0|aaa0|abb0|acb0|aab0|abc0|aaa0|abb0|acb0|\n","```\n","\n","程式碼  \n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","int compare_str_ptr(const void *a, const void *b)\n","{\n","    char **pa;\n","    char **pb;\n","    pa = (char **) a;\n","    pb = (char **) b;\n","    return strcmp(*pa, *pb);\n","}\n","\n","int main(void)\n","{\n","    char strs[SIZE][4] ={\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\",\n","        \"aab\", \"abc\", \"aaa\", \"abb\", \"acb\"\n","    };\n","    char *ptrs[SIZE];\n","    int i;\n","\n","    printf(\"strs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s0|\", strs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    for (i=0; i<SIZE; i++) {\n","        ptrs[i] = strs[i];\n","    }\n","\n","    printf(\"ptrs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%p|\", ptrs[i]);\n","    }\n","    printf(\"\\nafter sorting\\n\");\n","\n","    qsort(ptrs, SIZE, sizeof(char*), compare_str_ptr);\n","\n","    printf(\"ptrs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%p|\", ptrs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"ptrs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s0|\", ptrs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"strs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s0|\", strs[i]);\n","    }\n","\n","    return 0;\n","}\n","```"],"metadata":{"id":"QX9zHAJWY8rp"}},{"cell_type":"code","source":["%%writefile E15_03.c\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","int compare_str_ptr(const void *a, const void *b)\n","{\n","    char*   *pa;\n","    char*   *pb;\n","    pa = (char **) a;\n","    pb = (char **) b;\n","    return strcmp(*pa, *pb);\n","}\n","\n","int main(void)\n","{\n","    char strs[SIZE][10] ={\n","        \"aab\", \"abcc\", \"aaab\", \"abb\", \"acb\",\n","        \"aab\", \"abcaa\", \"aaa\", \"abb\", \"acb\"\n","    };\n","    char*  ptrs[SIZE];\n","    int i;\n","\n","    printf(\"strs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s0|\", strs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    for (i=0; i<SIZE; i++) {\n","        ptrs[i] = strs[i];\n","    }\n","\n","    printf(\"ptrs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%p|\", ptrs[i]);\n","    }\n","    printf(\"\\nafter sorting\\n\");\n","\n","    qsort(ptrs, SIZE, sizeof(char*), compare_str_ptr);\n","\n","    printf(\"ptrs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%p|\", ptrs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"ptrs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s0|\", ptrs[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"strs: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%s0|\", strs[i]);\n","    }\n","\n","    return 0;\n","}"],"metadata":{"id":"dfawDHbuZh7o","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1653392700290,"user_tz":-480,"elapsed":325,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"3b0e7436-ff65-45fd-c36e-64b57eef43b6"},"execution_count":8,"outputs":[{"output_type":"stream","name":"stdout","text":["Writing E15_03.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc E15_03.c -o E15_03\n","./E15_03"],"metadata":{"id":"HXbiKAdWZftR","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1653392704378,"user_tz":-480,"elapsed":14,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"323a51c5-84d1-4851-b9fa-8f653f5998f3"},"execution_count":10,"outputs":[{"output_type":"stream","name":"stdout","text":["strs: aab0|abcc0|aaab0|abb0|acb0|aab0|abcaa0|aaa0|abb0|acb0|\n","ptrs: 0x7ffc63f16ba0|0x7ffc63f16baa|0x7ffc63f16bb4|0x7ffc63f16bbe|0x7ffc63f16bc8|0x7ffc63f16bd2|0x7ffc63f16bdc|0x7ffc63f16be6|0x7ffc63f16bf0|0x7ffc63f16bfa|\n","after sorting\n","ptrs: 0x7ffc63f16be6|0x7ffc63f16bb4|0x7ffc63f16ba0|0x7ffc63f16bd2|0x7ffc63f16bbe|0x7ffc63f16bf0|0x7ffc63f16bdc|0x7ffc63f16baa|0x7ffc63f16bc8|0x7ffc63f16bfa|\n","ptrs: aaa0|aaab0|aab0|aab0|abb0|abb0|abcaa0|abcc0|acb0|acb0|\n","strs: aab0|abcc0|aaab0|abb0|acb0|aab0|abcaa0|aaa0|abb0|acb0|"]},{"output_type":"execute_result","data":{"text/plain":[""]},"metadata":{},"execution_count":10}]},{"cell_type":"markdown","source":["## Example 4\n","\n","## Pointers to Functions\n","\n","[The GNU C Programming Tutorial 對於 function pointers 的解釋](http://www.crasseux.com/books/ctutorial/Function-pointers.html)  \n","\n","```C\n","void qsort( void *ptr, size_t count, size_t size, int (*comp)(const void *, const void *) )\n","```\n","\n","以`qsort`來說，當初這個函式是被設計成對不同型別的資料都能進行排序  \n","在這邊可以理解成，其實這個函式根本不知道它正在排序的型別是什麼 (`ptr`的型別對`qsort`內部來說是`void*`)  \n","它只知道一個元素多大、總共有幾個元素和呼叫者提供的函式可以比較兩個元素的先後順序  \n","要傳入的`comp`才是實際上知道元素的型別，並如何比較兩者大小的關鍵  \n","\n","所以，如果想要設計一種函式，是希望能對應不同需求，執行使用者根據規定所設計出的函式  \n","這種時候就可以用函數指標，為程式帶來更大的彈性  \n","以`qsort`來說，它對使用者要求`comp`就是接收兩個只能讀取的`void*`指標，並回傳`int`代表比較結果的函式\n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","int sum(int a[], int n)\n","{\n","    int i, ans = 0;\n","    for (i=0; i<n; i++) {\n","        ans += a[i];\n","    }\n","    return ans;\n","}\n","int sum_squared(int a[], int n)\n","{\n","    int i, ans = 0;\n","    for (i=0; i<n; i++) {\n","        ans += a[i]*a[i];\n","    }\n","    return ans;\n","}\n","\n","int middle(int a[], int n)\n","{\n","    return a[n/2];\n","}\n","\n","int run (  int  (*fp) (int *, int )  , int * a, int n)\n","{\n","    return fp(a, n);\n","}\n","\n","int main(void)\n","{\n","    int a[] = {1, 2, 3, 4};\n","    printf(\"%d\\n\", run(sum, a, 4));\n","    printf(\"%d\\n\", run(sum_squared, a, 4));\n","    printf(\"%d\\n\", run(middle, a, 4));\n","    return 0;\n","}\n","```\n","\n","從上例中，可以看出函式指標的語法：  \n","*   若要宣告一個指向函式的指標，可以寫`return _type (*pointer_name) (parameter_list)`  \n","    而那個指標的型別是`return _type(*)(parameter_list)`  \n","*   而一個函式可以被隱式地轉換為指向該函式的指標，也可以透過`&function_name`來取得函數位址  \n","    所以如下兩個都是合法的：\n","    *   `run(sum, a, 4));`  \n","    *   `run(&sum, a, 4));`\n","*   要使用該函式指標的話，可以有如下兩種用法：\n","    *   `fp(a,n);`\n","    *   `(*fp)(a,n);`\n","\n","> Note:  \n","> [Stack Overflow 關於函式指標語法的有趣問答](https://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as)  \n","> [如何解讀型別](https://cseweb.ucsd.edu/~ricko/rt_lt.rule.html)  "],"metadata":{"id":"ZdkToitxZKvd"}},{"cell_type":"code","source":["%%writefile E15_04.c\n","#include <stdio.h>\n","#include <stdlib.h>\n","#include <string.h>\n","#define SIZE 10\n","\n","void mysort(void *ptr, size_t count, \n","            size_t size, int (*comp) (const void *, const void *) );\n","\n","\n","int compare_int(const void *a, const void *b)\n","{\n","    const int *va = (const int *) a;\n","    const int *vb = (const int *) b;  \n","    return *va-*vb;\n","}\n","\n","int compare_double(const void *a, const void *b)\n","{\n","    const double *da = (const double *) a;\n","    const double *db = (const double *) b;\n","    return (*db < *da) - (*db > *da);\n","}\n","\n","int main(void)\n","{\n","    int data1[SIZE];\n","    double data2[SIZE];\n","    int i;\n","\n","    for (i=0; i<SIZE; i++) {\n","        data1[i] = rand()%SIZE;\n","        data2[i] = (double)rand()/RAND_MAX;\n","    }\n","\n","    printf(\"original: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%d \", data1[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"  sorted: \");\n","    mysort(data1, SIZE, sizeof(int), compare_int);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%d \", data1[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"original: \");\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%.2f \", data2[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    printf(\"  sorted: \");\n","    mysort(data2, SIZE, sizeof(double), compare_double);\n","    for (i=0; i<SIZE; i++) {\n","        printf(\"%.2f \", data2[i]);\n","    }\n","    printf(\"\\n\");\n","\n","    return 0;\n","}\n","\n","void* addr(char* r, size_t size, int idx)\n","{\n","   return r + size*idx; \n","}\n","\n","void swap(char* r, size_t size, int idx1, int idx2)\n","{\n","  int i;\n","  char tmp;\n","  for (i=0; i<size; ++i) {\n","    tmp = r[idx1*size + i];\n","    r[idx1*size + i] = r[idx2*size + i];\n","    r[idx2*size + i] = tmp;\n","  }   \n","}\n","\n","void mysort(void *p, size_t count, \n","            size_t size, int (*comp) (const void *, const void *) )\n","{\n","  int i = 1, j;\n","  int tmp;\n","  char * pc;\n","  pc = (char *) p;\n"," \n","  while (i < count) {\n","    j = i;\n","    while (j > 0 && comp( addr(pc, size, j-1) , addr(pc, size, j) ) > 0) {\n","        swap(pc, size, j, j-1);\n","        --j;\n","    }\n","    ++i;\n","  }\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"e8OhhF4rE68h","executionInfo":{"status":"ok","timestamp":1653397493384,"user_tz":-480,"elapsed":563,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"87e76080-ed28-4fe6-d6b9-85f87461c75e"},"execution_count":13,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting E15_04.c\n"]}]},{"cell_type":"code","source":["%%shell\n","gcc E15_04.c -o E15_04\n","./E15_04"],"metadata":{"id":"lA2fVwzDZstH","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1653397497822,"user_tz":-480,"elapsed":374,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"d0b9544e-b8c4-430f-d8f9-e8749ce5c7e6"},"execution_count":14,"outputs":[{"output_type":"stream","name":"stdout","text":["original: 3 7 3 6 9 2 0 3 0 2 \n","  sorted: 0 0 2 2 3 3 3 6 7 9 \n","original: 0.39 0.80 0.20 0.77 0.55 0.63 0.51 0.92 0.72 0.61 \n","  sorted: 0.20 0.39 0.51 0.55 0.61 0.63 0.72 0.77 0.80 0.92 \n"]},{"output_type":"execute_result","data":{"text/plain":[""]},"metadata":{},"execution_count":14}]},{"cell_type":"markdown","source":["## 在程式執行期間取得記憶體\n","[C reference 對於 malloc 的說明](http://en.cppreference.com/w/c/memory/malloc)  \n","[C reference 對於 free 的說明](http://en.cppreference.com/w/c/memory/free)  \n","\n","有的時候，我們需要在執行的時候動態分配記憶體  \n","此時可以使用`malloc`跟`free`進行記憶體的分配與釋放  \n","\n","`void* malloc( size_t size )`  \n","*   *size*  \n","    要分配的記憶體大小，以 byte 為單位  \n","*   *回傳值*  \n","    如果成功的話，回傳的指標是指向分配的的記憶體的開頭位址  \n","    如果失敗的話，回傳一個空指標\n","\n","`void free( void* ptr )`  \n","*   *ptr*  \n","    要釋放的記憶體的指標，必須是當初分配某塊空間時拿到的那個位址；如果`ptr`為`NULL`，則此函式不會做任何事情  \n","    譬如呼叫`malloc(16);`以後拿到的位址是`0x8BADF00D`，之後若想要將這塊塊記憶體回收  \n","    在呼叫`free`時必須傳入`0x8BADF00D`，不能是`0x8BADF00F`或其他不是由相關記憶體分配函式所回傳的位址  \n","\n","> Note:  \n","> `void*`的意思是指向不特定的型別，通常用來記錄某個實體是位在記憶體的哪裡  \n","> 所以針對`void*`是不能進行運算及間接引用  \n","> 在轉型時要特別注意，通常是建議如果將`T*`轉為`void*`，最後要間接引用時，應轉回`T*`而非其他型別  \n","> `int*`->`void*`->`int*`會得到與原本相同的位址，最後間接取值時會正確  \n","> `int*`->`void*`->`float*`不一定會得到與原本相同的位址，最後間接取值時的結果也不一定會正確  \n","> [Stack Overflow 關於指標轉換的問答](https://stackoverflow.com/questions/4810417/c-when-is-casting-between-pointer-types-not-undefined-behavior)  \n","> [Stack Overflow 關於 void* 轉換的問答](https://stackoverflow.com/questions/20469958/c-when-is-casting-void-pointer-needed) \n"," \n","```C\n","/* E10_15.c */\n","#include <stdio.h>\n","#include <stdlib.h>\n","int main(void)\n","{\n","   double *ptd;\n","   int array_size, i;\n","\n","   printf(\"How many doubles do you want? \");\n","   scanf(\"%d\", &array_size);\n","   ptd = (double *) malloc(array_size * sizeof (double));\n","   if (ptd == NULL) {\n","      printf(\"Memory allocation failed.\\n\");\n","      exit(EXIT_FAILURE);\n","   }\n","\n","   for (i = 0; i < array_size; i++) {\n","      ptd[i] = (double) rand() / RAND_MAX;\n","   }\n","   for (i = 0; i < array_size; i++) {\n","      if (*(ptd+i) > 0.5)\n","         printf(\"%d: %f\\n\", i, ptd[i]);\n","   }\n","\n","   free(ptd);\n","\n","   return 0;\n","}\n","```\n","\n","```C\n","/* W10_09.c */\n","#include <stdio.h>\n","int main(void)\n","{\n","   int *a, **b, i, j, rows, cols;\n","   \n","   scanf(\"%d %d\", &rows, &cols);\n","   a = (int *) malloc(rows * cols * sizeof(int));\n","   for (i = 0; i < rows*cols; i++) a[i] = i;\n","   b = (int* *) malloc(rows * sizeof(int *));\n","\n","   for (i = 0 ; i < rows; i++) b[i] = &a[i*cols];\n","\n","\n","   for (i = 0; i < rows; i++) {\n","      for (j = 0; j < cols; j++) {\n","         printf(\"%3d \", b[i][j]); \n","      }\n","      printf(\"\\n\");\n","   }\n","   \n","   free(b);\n","   free(a);\n","\n","   return 0;\n","}\n","```\n","\n","關於動態分配記憶體時，可能會犯的一些錯誤：  \n","*   memory leak  \n","    ```C\n","    int* a = malloc(sizeof(int));\n","    *a = 123;\n","    a = malloc(sizeof(int));\n","    *a = 456;\n","    free(a);\n","    ```  \n","    以上例來說，第一次分配到的記憶體就這樣被丟失，沒有被回收，之後也沒有任何方法能夠回收到那塊記憶體  \n","    類似這種狀況，當有記憶體是沒辦法被回收的，就被稱為 memory leak  \n","\n","*   dangling pointer  \n","    ```C\n","    int* ptr = NULL;\n","    {\n","        int a = 0;\n","        ptr = &a;\n","    }\n","    // ptr is a dangling pointer now !\n","    ```  \n","    以上例來說，`int a`的生命週期只在那對大括弧內，一旦過了那個大括弧的範圍，`ptr`所指向的位址就是不合法的  \n","    類似這種狀況，當有指標指向的記憶體是已經無效的，該指標就被稱為 dangling pointer  \n","    ```C\n","    int* a = malloc(sizeof(int));\n","    int* b = a;\n","    *a = 123;\n","    \n","    free(a);\n","    a = NULL;\n","    \n","    // b is a dangling pointer now !\n","    ```  \n","    > Note:  \n","    > 對大多數`malloc`和`free`的 implementation 來說，所謂取得和釋放其實只是改變記憶體的使用狀態  \n","    > `malloc`和`free`做的事情只是去記錄哪些記憶體區塊被佔用以及哪些可用  \n","    > 所以呼叫`free(a)`之後，乍看之下可能會覺得沒發生什麼變化，因為原先那些記憶體內的資料可能都還存在  \n","    > 但是無論如何程式都不該再去存取已經被釋放的記憶體內容  \n","\n","## 字元陣列和字串\n","[C reference 對於 string literal in C 的說明](http://en.cppreference.com/w/c/language/string_literal)  \n","[C++ reference 對於 string literal in C++ 的說明](http://en.cppreference.com/w/cpp/language/string_literal)  \n","[Stack Overflow 關於 string literal 在 C 與 C++ 中的說明](https://stackoverflow.com/questions/2245664/what-is-the-type-of-string-literals-in-c-and-c)  \n","\n","字元陣列其實就是一個陣列，其元素為字元  \n","而字串則是一個字元陣列，且其結尾必須是`'\\0'`  \n","通常在傳遞字串時，會用指向字串的第一個字元的指標作為該字串的代表  \n","\n","關於 string literal，有幾個需要注意的地方：  \n","1.  string literal 的型別是 `char[]`  \n","2.  如果是用指標去指向某個 string literal，那麼其指向的內容是不可寫的  \n","    ```C\n","    char* str = \"Test\"; // OK\n","    str[0] = 't'; // 未定義行為，但可以通過編譯，可能在執行時出錯\n","    ```\n","3.  為了避免類似錯誤發生，通常會建議用：  \n","    *   ```C\n","        const char* str = \"string\"; // 如果寫出 str[0]='t'，在編譯時就會出錯\n","        ```\n","    *   ```C\n","        char str[] = \"string\"; // str[0]='S' 是可以的\n","        ```\n","\n","```C\n","const char* str = \"string\";\n","```\n","*   `\"string\"`會在程式被載入的時候，一起被載入到某塊記憶體 (常見的狀況是載入到 data segment)  \n","*   而`str`指向的位址就是`\"string\"`被儲存到的記憶體的開頭位址  \n","*   標準規定對於 string literal 的寫入是未定義行為，實際上也有實作是將`\"string\"`載入到一塊只允許讀取的記憶體  \n","\n","```C\n","char str[] = \"string\";\n","```  \n","*   其實該句等同`char str[7] = \"string\"`，也等同於`char str[7] = {'s', 't', 'r', 'i', 'n', 'g', '\\0'};`  \n","*   相當於把記憶體中`\"string\"`複製一份當作`str`這個陣列的初始值  \n","*   所以`str[0] = 'S';`就合法了  \n","\n","試著執行看看下列的程式碼，並了解`str1`與`str2`之間的差別，以及輸出是否如同預期\n","\n","```C\n","#include <stdio.h>\n","\n","int main(void)\n","{\n","\n","    char *str1[] = {\"piece\", \"of\", \"cake\"};\n","\n","    char str2[][8] = {\"piece\", \"of\", \"cake\"};\n","\n","    int i, j;\n","\n","    for (i=0; i<3; i++) {\n","\n","        for (j=0; j<8; j++)\n","\n","            printf(\"%c\", str1[i][j]);\n","\n","        printf(\"\\n\");\n","\n","    }\n","\n","    for (i=0; i<3; i++) {\n","\n","        for (j=0; j<8; j++)\n","\n","            printf(\"%c\", str2[i][j]);\n","\n","        printf(\"\\n\");\n","\n","    }\n","\n","    return 0; \n","\n","}\n","```\n","\n","## 雙重指標\n","\n","相當於指標的指標  \n","如果想要透過一個函式更改外面的`int`變數，會有類似底下的寫法  \n","\n","```C\n","void swap(int* a, int* b)\n","{\n","    int temp = *a;\n","    *a = *b;\n","    *b = temp;\n","}\n","\n","...\n","\n","int x = 5, y = 3;\n","swap(&x, &y);\n","```  \n","\n","那如果想要透過一個函式更改外面的`int*`變數呢？  \n","觀察上面的程式，會發現如果要透過函式更改外面的變數，其型別為`T`  \n","那麼在函式的參數，會期待收到一個`T*`的引數，藉此可以透過間接引用的方式改到外面的變數  \n","\n","下例則是表達如果希望藉由函式改變外面的`ptr`，其型別為`float*`  \n","在這裡可以將`float*`理解成`T`，按照上面的推論，那麼必須要傳入型別為`float**`的引數，也就是`&ptr`  \n","才可以在函式裡面藉由`*p`更改`ptr`所指向的位址  \n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","\n","void malloc_float2( float * * p , unsigned int sz)\n","{\n","    *p = (float *) malloc(sz*sizeof(float));\n","}\n","\n","int main(void)\n","{\n","    float * ptr = NULL;\n","    int i;\n","    int n = 100;\n","\n","    malloc_float2(&ptr, n);\n","\n","    for (i=0; i<n; i++)\n","        ptr[i] = (float) rand()/RAND_MAX;\n","\n","    free(ptr);\n","\n","    return 0;\n","}\n","```\n","\n","如果希望有更具體的圖片參考，可以看`double_pointer.pdf`中的講解  \n","\n","---\n","\n","# 練習題\n","\n","## qsort-01\n","\n","用導向方式 (例如：`D:\\COURSE\\I2P\\code>W10_01.exe < test.txt`) 讀入一串整數，整數的個數不超過 100 個  \n","每個整數都介於 0 和 1,000,000，將資料存入陣列中，然後一併找出其中的最大值和最小值  \n","\n","用下列程式碼產生亂數檔`number.txt`  \n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","\n","int main()\n","{\n","    int ii,max;\n","\n","    FILE *random;\n","    random=fopen(\"number.txt\",\"w\");\n","\n","    printf(\"How many number do you want?\\n\");\n","    scanf(\"%d\",&max);\n","    for(ii=0;ii<max;ii++){\n","\t\tfprintf(random,\"%d\\n\",rand()%1000000);\n","    }\n","\n","    fclose(random);\n","    return 0;\n","}\n","```\n","    \n","用下列程式碼找出最大最小值  \n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","\n","int main()\n","{\n","    int total=0;\n","    int max=0, min=0;\n","    int number[100];\n","\n","    FILE *random;\n","\n","    random=fopen(\"number.txt\",\"r\");\n","\n","\n","    while(fscanf(random,\"%d\",&number[total])!=EOF)\n","\t{\n","\t\tif(max<number[total]) max=number[total];\n","\n","\t\tif(min>number[total]) min=number[total];\n","\t\ttotal++;\n","\t}\n","\n","\tprintf(\"The max is %7d\\n\",number[total-1]);\n","\tprintf(\"The min is %7d\\n\",number[0]);\n","\n","    return 0;\n","}\n","```\n","\n","## qsort-02\n","\n","和 [qsort-01](#qsort-01) 的輸入資料相同，將資料從小到大按照順序排好  \n","\n","用下列程式碼將數列排序  \n","\n","```C\n","#include <stdio.h>\n","#include <stdlib.h>\n","\n","int compare(void *a,void *b)\n","{\n","\tint *c=a;\n","\tint *d=b;\n","\n","\tif(a>b) return 1;\n","\telse\treturn 0;\n","}\n","\n","int main()\n","{\n","    int total=0;\n","    int number[100];\n","\n","    FILE *random;\n","\n","    random=fopen(\"number.txt\",\"r\");\n","\n","\n","    while(fscanf(random,\"%d\",&number[total])!=EOF)\n","\t{\n","\t\ttotal++;\n","\t}\n","\n","\tqsort(number,total,sizeof(int),compare);\n","\n","\tprintf(\"The max is %7d\\n\",number[total-1]);\n","\tprintf(\"The min is %7d\\n\",number[0]);\n","\n","    return 0;\n","}\n","```\n","\n","## 在程式執行期間取得記憶體-01\n","\n","解釋下面的程式碼，並描述會得到什麼樣的輸出結果  \n","\n","```C\n","#include <stdio.h>\n","\n","int main(void)\n","{\n","    int *a, **b, i, j, rows, cols;\n","    scanf(\"%d %d\", &rows, &cols);\n","    \n","    a = (int *) malloc(rows * cols * sizeof(int));\n","    for (i = 0; i < rows*cols; i++) a[i] = i;\n","\n","    b = (int **) malloc(rows * sizeof(int *));\n","    for (i = 0 ; i < rows; i++) b[i] = &a[i*cols];\n","    \n","    for (i = 0; i < rows; i++) {\n","        for (j = 0; j < cols; j++) {\n","            printf(\"%3d \", b[i][j]);\n","        }\n","    printf(\"\\n\");\n","    }\n","\n","free(b);\n","free(a);\n","\n","return 0;\n","}\n","```\n","\n","## 在程式執行期間取得記憶體-02\n","\n","宣告一個`struct t_point`包含`int x`和`int y`  \n","讓使用者輸入 n 和 k 兩個整數，並用`malloc`產生一個型別為`struct t_point`，長度為 n 的陣列，取名為`P`  \n","隨機設定每個`P[i]`的`x`和`y` (設定成介於 0 ~ k-1 的整數，總共有 k*k 個格子)，並把落在每個格子內的點的數量顯示出來  \n","最後把`P`佔用的記憶體釋放掉  \n","\n","## 字元陣列和字串-01\n","\n","說明並圖示下面兩種寫法的差異  \n","\n","```C\n","char str1[] = \"piece of cake\";\n","char *str2 = \"piece of cake\"; /* 附註：ANSI C 要求 compiler 可以支援到長度 509 的字串`\n","```\n","\n","## 字元陣列和字串-02\n","\n","假設在`main`裡面有下列三行，請問`str1`、`str2`、`str3`的內容會是什麼？  \n","\n","```C\n","char str1[100];\n","char str2[100] = {'a'}; \n","char str3[100] = \"\";\n","```\n","\n","## 字元陣列和字串-03\n","\n","寫一個 function，傳入一個字串，判斷是否為 palindrome (像是`level`、`wasitacatisaw`)  \n","\n","## 字元陣列和字串-04\n","\n","用圖示表現出下面的字串  \n","\n","```C\n","char *ptrary[] = {\"piece\", \"of\", \"cake\"};\n","```\n","\n","## 字元陣列和字串-05\n","\n","圖示下面兩種寫法的差別  \n","\n","```C\n","char *str1[] = {\"piece\", \"of\", \"cake\"};\n","char str2[][8] = {\"piece\", \"of\", \"cake\"};\n","```\n","\n","## 雙重指標-01\n","\n","寫一個 function 叫做`sort`，它的作用是讓兩個變數`a`和`b`在經過呼叫`sort`之後  \n","`a`的值變成`a`和`b`之中較小的值，`b`的值變成`a`和`b`之中較大的值  \n","試著用指標和位址存取方式來寫  \n","\n","## 雙重指標-02"],"metadata":{"id":"cTi0xWPLbuIt"}}]}