# 2017q1 Homework5 (matrix) contributed by < `ryanwang522` > ## 開發環境 - OS: ubuntu 16.04 LTS - L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K - Architecture:x86_64 - CPU op-mode(s): 32-bit, 64-bit - Byte Order: Little Endian - CPU(s): 4 - Model name: Intel® Core™ i5-4200H CPU @ 2.80GHz ## 開發紀錄 ### matrix.h * 看不太懂 macro 的寫法,參考 [Concatenation](https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html) 的說明,利用 `##` operator 連接字串,讓宣告更為方便。 ```C= struct command commands[] = { { "quit", quit_command }, { "help", help_command }, … }; ``` * 例如上面這段 code 可以替換成 ```c= #define COMMAND(NAME) { #NAME, NAME ## _command } struct command commands[] = { COMMAND (quit), COMMAND (help), … }; ``` 所以原本 `matrix.h` 裡的寫法就可以替換成兩次的 ```C= typedef struct { float values[3][3]; } Mat3x3 typedef struct { float values[4][4]; } Mat4x4 ``` --- ### 嘗試支援不同的 data type 和 matrix size * 剛看完 macro 的資料就想說來練習怎麼使用好了,雖然直接寫函式比較直觀,不過就試試看吧~ ```clike #define DECLARE_MATRIX_FLEXIBLE(type, cols, rows) \ type **mat ## cols ## rows = (type **) malloc(sizeof(type *) * rows); \ for (int i = 0; i < rows; i++) \ mat ## cols ## rows[i] = (type *) malloc(sizeof(type) * cols) DECLARE_MATRIX_FLEXIBLE(float, 4, 4); ``` * 一開始的想法,後來弄懂 macro 後才發現這樣寫不太 ok * 上面這段 code 會產生 `initializer element is not constant` 的編譯錯誤,參考網路上的資料應該是對全域變數進行了不合法的初始化 * 嘗試將要執行的程式碼放在函式,並藉由 define 宣告函式,在 define 一個額外的函式呼叫 ```C= #define DECLARE_MATRIX_TYPE(type) \ static type **matrix_##type(int cols, int rows) { \ type **mat ## cols ## rows = (type **) malloc(sizeof(type *) * rows); \ for (int i = 0; i < rows; i++) \ mat ## cols ## rows[i] = (type *) malloc(sizeof(type) * cols); \ return mat ## cols ## rows; } DECLARE_MATRIX_TYPE(float); #define CREATE_MATRIX(type, cols, rows) \ type **type##_mat##cols##x##rows = matrix_##type(cols, rows) ``` * 藉由 `DECLARE_MATRIX_TYPE(float)` 定義可以動態配置一個二維陣列的 function,接著只要在 `.c` 檔呼叫 `CREATE_MATRIX(float, 4, 4)` 就可以產生一個 `float **` 型態的變數 `float_mat` * 不過有個嚴重的問題就是因為變數名稱寫死所以只能用一次...,雖然覺得做了不少白工,但沒關係就當作個經驗吧XD #### 修改 `matrix_naive.c` 的 `assign` 函式 * 遇到問題! * 如果同時要支援不同 data type ,函式的參數型態會無法區分 * 先實作支援不同大小的矩陣 * 直接傳 2D Array --- ### 實作支援不同大小的矩陣 * 把之前嘗試的 macro 拿掉,修改 `main()` * 動態配置二維陣列作為 matrix * 捨棄先前直接寫死的矩陣,產生亂數矩陣方便測試 * 增加一些方便的函式 ```C= float **alloc(int row, int col) { float **result; if (!(result = (float **) malloc(row * sizeof(float *)))) return NULL; for (int i = 0; i < col; i++) if (!(result[i] = (float *) malloc(col * sizeof(float)))) { free(result); return NULL; } return result; } void free_space(float ***target, int col) { for (int i = 0; i < col; i++) free((*target)[i]); free(**target); **target = NULL; } ``` * 學到了如何亂數產生浮點數,記得要加上 `srand(time(NULL))` ```C= float randFloat(float range) { return ((float)rand() / (float)(RAND_MAX)) * range; } ``` * 接著修改 `assign` 函式如下 ```C= static void assign(Matrix *thiz, int rows, int cols, float **data) { thiz->row = rows; thiz->col = cols; thiz->priv = malloc(cols * rows * sizeof(float)); PRIV(thiz)->values = (float **) malloc(rows * sizeof(float *)); for (int i = 0; i < cols; i++) PRIV(thiz)->values[i] = (float *) malloc(cols * sizeof(float)); for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) PRIV(thiz)->values[i][j] = data[i][j]; } ``` * 這裡有個問題... * `thiz->priv = malloc(cols * rows * sizeof(float));` * 這裡配置給 `void *priv` 一塊記憶體,在將 `thiz->priv` cast 成 `struct naive_priv *` ,但不懂為什麼要給 cols * rows 的大小呢 * 先紀錄一下,再想想... * 要注意 values 是宣告成 `float **` ,要記得 malloc 空間給她否則直接存取會造成 segmentation fault --- ## 參考資料 - [Concatenation](https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html) - [ptt - 第一次使用亂數就上手](https://www.ptt.cc/man/C_and_CPP/DB9B/DE78/M.1198667412.A.173.html)