# W6 ~ W11 Memory ##### `Cs50` ## Link : [Memory](https://youtu.be/AcWIE9qazLI?list=PLhQjrBD2T380F_inVRXMIHCqLaNUd7bN4&t=2099) # Notes ::: spoiler tomas decimal system = 十進制 hexadecimal system = 16 進制 ambiguity = 崎意 `0 ~ F` 16 進制 : 為了更簡潔的表示數字,並且可以和固定數量的二進制位對上,最大差異在於基數的不同。 用途: 常用於表示記憶體位置。 ambiguity : 10 會被認為是十,所以在十六進制中會在第一個數字前使用 0x 表示正在使用十六進制。 ex : ``` 01 => 0x01 ``` **demo** : - 在大多數現代的操作系統中,一個整數(int)占用 4 個字節(bytes)。 ```c #include <stdio.h> int main(void) { int n = 50; printf("%i\n",n) } ``` 在上面這段 code 被執行後,n 將會被存放於某處記憶體中進行儲存。 ### 運算符 `&*` : `&`這個運算符可以獲取記憶體中的一段數據的位址,只要在變量名稱前加上`&`, c 就可以獲得這個變量的位址。 `*`解引用運算符,這個運算符表示將目標變數轉至特定位址。 **demo** : - 使用 `%p`可以印出記憶體位址。 - 這時候印出的將會是 n 的位址。 ```c #include <stdio.h> int main(void) { int n = 50; printf("%p\n",n) } //0xffcc784a04c ``` ### pointer in c 本質上為一個儲存位址的變量。 **demo**: 宣告的時候在該變數前方加上 `*`表示這個變數是一個指針(pointer) ````c int n = 50; int *p = &n; //p 是一個 pointer 並且 value 為透過 ```&``` 所獲取之 n 的位址 ```` ````c #include <stdio.h> int main(void) { int n = 50; int *p = &n; //p 是一個 pointer 並且 value 為透過 ```&``` 所獲取之 n 的位址, pointer 通常占用 8 個字節。 printf("%p\n",p) } ```` pointer 可視作一個指向其他東西的變量。 ![pointer.png](https://hackmd.io/_uploads/SJWC90kmp.png) ### pointer with string **demo** : ```c string s = "HI!"; ``` ![image.png](https://hackmd.io/_uploads/HkSi30k7T.png) 單 & 雙引號的差異: `''` : 單引號只儲存一個字符 `""` : 雙引號會自動在最後方加上一個 \0 **string s** : ![image.png](https://hackmd.io/_uploads/BJWST0176.png) ![image.png](https://hackmd.io/_uploads/ryoO6Ry76.png) - 為什麼 string 只需儲存 第一個 char 的位址? - 因為 string 總是以 \0 做結尾,所以只需要從第一個位址開始尋找,直到\0 為止 ![image.png](https://hackmd.io/_uploads/rkh7RCkmT.png) ![image.png](https://hackmd.io/_uploads/HyxH0RJm6.png) 實際上這邊的 s 就是做為指向 string 中的第一個 char 位址的 pointer * **example**: ```c #include <cs50.h> #include <stdio.h> int main(void) { string s = "HI!"; //c 實際上沒有 string 這個關鍵字 應該是 char *s = "HI!" printf("%s\n",s) } //HI! ``` 上面這段 code 實際上做的事情,就是透過 pointer \*s 儲存第一個 char 的位址。 ```c typedef char *string ``` ### 宣告型別的方式 ```c typedef int integer //這邊的 integer 為自訂名稱 表示 integer 的型別是 int ``` ### 另一個訪問位址的方式 ```c #include <stdio.h> int main(void) { int n = 50; int *p = &n; //這個情境下這條無用 printf("%i\n",*p); //改成 i 表示現在要 print 的不是一個位址 而是一個 value, 並且加上 * 表示前往 p 所儲存的位址,並且會獲得該位址的 value。 } ``` ### `%p`表示 print pointer value, `%s` 表示前往該位址 ```c #include <stdio.h> int main(main) { // string s = "HI!"; can't without CS50 library char *s = "HI!"; // 表示字串 s printf("%p\n",s) //s 的 memory address printf("%s\n",s) // HI! // 前往 s 的位址並往後遍歷直到空字符\n // 若是使用 *s 則會獲得該位址的第一個字符 printf("%p\n",&s) //print s 的 memory address printf("%p\n",&s[0]) //print s 中第一個字元的 memory ~~address~~ printf("%p\n",&s[1]) //print s 中第二個字元的 memory address === 第一個字元 + 1 } ``` ### pointer 運算 ### string 比對 ```c #include <cs50.h> #include <stdio.h> int main(void) { string s = get_string ("s: "); string t = get_string ("t: "); if(s == t) //這邊比對的是記憶體位址 { printf("Same\n") } else { printf("Diff\n") } } ``` #### 為什麼需要使用`strcmp` - 透過 strcmp 實際上是使用兩個指針 分別從被比較的字串最左邊開始比較 ```c #include <cs50.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string ("s: "); string t = get_string ("t: "); if(strcmp(s,t) == 0) { printf("Same\n") } else { printf("Diff\n") } } ``` - 不使用的情況下 ```c #include <cs50.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string ("s: "); string t = get_string ("t: "); char *s = get_string ("s: "); char *t = get_string ("t: "); printf("%s\n, s"); //查看變量 printf("%s\n, t"); printf("%p\n, s"); //查看位址 printf("%p\n, t"); } ``` ## 複製字串 ### 新增兩種 library - malloc : 用於記憶體分配 return 一個 value ,會找到閒置記憶體的第一個位址 - free : 用於釋放記憶體 傳遞一個 pointer 表示釋放這個位址的記憶體 ```c #include <cs50.h> #include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string ("s: "); string t = s; //這邊複製的是位址 t[0] = toupper(t[0]); if (strlen(t) > 0) // strlen(t) => t的長度 > 0 - fixed { t[0] = toupper(t[0]); // 轉為大寫 } printf("s: %s\n, s"); printf("t: %s\n, t"); } ``` fixed: ```c #include <cs50.h> #include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { char *s = get_string ("s: "); //origin if(s == NULL) // handle error { return 1; //表示 error } char *t = malloc(strlen(s) + 1); //請求記憶體 = s的長度 - malloc //+1 是為了終止符號(/0)的空間 char *t = s; //t的位址 = s的位址 所以s的變動會影響t - origin if(t == NULL) { return 1; for(int i = 0; n = strlen(s) + 1; i < sLength; i++) // fixed : 增加 n 避免條件中有函式 { t[i] = s[i] } //等同於 strcpy(t,s); if( strlen(t) > 0 ) // or t[0] !== NULL //(0) { t[0] = toupper(t[0]); // 轉為大寫 } printf("s: %s\n, s"); printf("t: %s\n, t"); // 使用 malloc 時 函數結尾需要釋放記憶體 free(t); return 0 } ``` ### check memory error valgrind : ```c valgrind ./fileName ``` ### inilize value 不重置的話會導致其中包含無效value ### point & pointee point ```c int *x; ``` pointee //需要獨立步驟 ```c x = malloc(sizeof(int)) *x = 13 ``` ### 記憶體順序 machine code galbals heap > 記憶體堆 malloc就是從這邊分割出請求的記憶體空間 並且 malloc 會追蹤被分配的字節 //當這個區塊被佔滿就會導致crash //heap overflow & stack overflow // stack 從最下方開始往上使用記憶體空間 //function 被宣告的時候會佔用這個區塊 ### swap value c 中 想調換資料的時候不能直接透過 ```=``` 需要透過位址進行資料替換 當透過 function 在進行替換的時候 會因為遇到作用域的問題 導致結果不如預期 ### 操作 ```&```表示取得記憶體位址 ```*```表示前往目標位址 ### 還原 cs50 library get_int ```c int x; printf("x: "); scanf("%i", &x); printf("x: %i\n", x); //hanndle error ``` get_string ```c // char *s = NULL; // inilize value chat s[4]; printf("s: "); scanf("%s", s); // string always was a address printf("s: %s", s) ``` ::: ::: spoiler KellyLee **十進制:** 1111 (二進制表15)轉成十進制的算法 $$(1 \times 2^3) + (1 \times 2^2) + (1 \times 2^1) + (1 \times 2^0)$$ =8+4+2+1=15 1001 (二進制表)轉成十進制的算法 $$(1 \times 2^3) + (1 \times 2^0)$$ =8+1=9 ![Untitled.png](https://hackmd.io/_uploads/rkrI1tgQa.png) **十六進制 hexadecimal (base-16):** 0123456789ABCDEF ( 00 ~ FF ) 11111111(二進制) 代表 FF (16進制) 一個十六進制數在電腦始終表示4 bits 11111111 在十進制表示 255 在photoshop,0000FF意味著沒有紅色,沒有綠色,藍色有255個. **copy.c:** ```c #include <cs50.h> #include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { string s = get_string("s: "); string t = s; t[0] = toupper(t[0]); printf("%s\n", s); printf("%s\n", t); } ``` 在這段程式碼中,s 和 t 是指向不同內存位置的指標。當 string t = s; 被執行時,t 指向了 s 所指向的內存位置,因此它們兩者指向的是同一個字串的內容。當你更改 t 中的內容時,實際上也會改變 s 指向的內容,因為它們指向相同的記憶體位置。