# memset為何那樣? > 作者: Aria > Oct. 13, 2024 > 本文部分內容翻譯自[[1]]() # 定義 (Define) ```c= void *memset(void *s, int c, size_t n); ``` ## 功能 複製第二個argument的值 (以Byte為單位)到第一個argument所指向的前n Bytes. ## 應用場景 1. 使用memset來將array清為$0$, 比逐一指定每個值為$0$還要好。 2. 硬體架構需要將整個block的清零或寫入時。 ## 注意事項 建議使用 sizeof 來確保不會超出array邊界,例如: ```c= memset(array, 0, sizeof(array)); ``` ### 舉例1. 1.1 要將一個具有$5$個元素的陣列n 的每個元素都指定為$0$: ```c= memset(n, 0, 5); ``` 1.2 ```c= // fig08_25.c // Using function memset #include <stdio.h> #include <string.h> int main(void) { char string1[15] = "BBBBBBBBBBBBBB"; printf("string1 = %s\n", string1); printf("string1 after memset = %s\n", (char *) memset(string1, 'b', 7)); } ``` :::success string1 = BBBBBBBBBBBBBB string1 after memset = bbbbbbbBBBBBBB ::: ## memset有漏洞嗎?為何在MISRA中被建議不要使用? 1. 可能指到空指標 2. Byte運算過於複雜 ### 舉例2. 2.1 以[2]為例: ```c= char path[25500]= {};//iniatize int visited[2500]= {0};//iniatialize void fun();//protype int main(){ fun(); } void fun(){ visited[2300]=1; path[0]='9'; printf("\n%d %s",visited[2300],path); memset(path,0,25500);//reset memset(visited,0,2500);//reset printf("\n%d %s",visited[2300],path);//to see if resetted } ``` 2.1.1. 第13行:使用memset, 將$0$複製到path所指向的陣列的前$25500$ Byte, 預期會讓path的前$25500$ Byte為$0$, 又path是char型態,所以sizeof(path[0])===$1$==(<font color="ff0000"> char 型態佔用1Byte</font>), 前25500 Byte就是path陣列的第$0$~$25499$項,都被清為$0$. 2.1.2. 第14行:使用memset, 將$0$複製到visited所指向的陣列的前$2500$ Byte, 預期會讓visited的前$2500$ Byte為$0$, 又visited是int型態,所以sizeof(visited[0])=4, 前$2500$ Byte就是visited陣列的第$0$~$624$項,都被清為$0$. 2.1.3. [2]中,原po的問題是:為什麼第15行,path印出來是==空白==呢? - 在[ideone ([6])](https://ideone.com/)中執行: ```C= printf("\npath=%s, path=%d",path,path); ``` :::success path=, path=1157945184 ::: - - path[$0$]目前的值為$1157945184$. - 若換算成字元型態:在ideone中跑出來是==null==。 - 為何path[$0$]目前的值為$1157945184$呢? 2.1.4. 由[3]可知: - printf中%s格式期望一個以 null 結尾的字串。<font color="ff0000">(待查證,詳見補充2)</font> - 在 C 語言中,當我們嘗試將一個整數值當成字串印出時,printf 會將該整數解釋為指向字串的指標。<font color="ff0000">(待查證,詳見補充2)</font> - Key point在於最後的 printf 語句。當 printf 遇到 %s 時,它將 path 解釋為一個指向字串的指標。由於 path 已經被清為全零,printf 實際上在讀取一個值為 0 的指標。 在大部分的系統中,試圖讀取地址 0 處的記憶體是非法的,這可能導致==未定義行為==。在這種情況下,printf 可能讀取到了一些隨機的記憶體內容,並將其解釋為一個整數值。 $1157945184$ 這個數字很可能是記憶體中的一筆隨機數據,或者是由於未定義行為而產生的結果。 ### 建議的解決方法: 為了避免這種情況,可以將 printf : 原本: ```C= printf("\npath=%s, path=%d",path,path); ``` 修改為: ```C= printf("\npath=%d, path=%p", path[0], (void*)path); ``` :::success path=0, path=0x556a17db4760 ::: 這看起來像是一個地址。 ## 建議的解決方法: :::info 在使用 memset 後,手動設置字串的結束符: ```c= memset(path, 0, 25500); path[0] = '\0'; // 確保字符串正確終止 ``` 這樣可以避免==未定義行為==,並確保 printf 正確處理 path. ::: # 觀眾提問 1. memset只能指定"**定值**"嗎? 是,如果你要指定一個字串給array,可以用**memcpy**. # 補充 1. 字串要用雙引號。 字元要用單引號。 2. printf 待查證。 # 後記 最近看了[4]和[5]又讓我對memset有更深一層認識!甚麼是未定義行為, (void *)又是甚麼意思? 0的指標指向哪? memcpy可以怎麼使用? 待之後比較有空再來做整理、查證、補充! ## Reference [[1] Paul Deitel , Harvey Deitel ".8.9.5 Function memset" C How to Program: With Case Studies in Applications and Systems Programming, 9/e (GE-Paperback), Pearson FT Press, Aug. 5, 2022 [Oct. 13, 2024]](https://www.tenlong.com.tw/products/9781292437071) [[2] user3683963 ".memset does not work (C )" Stack Overflow, Jun. 8, 2014 [Oct. 13, 2024]](https://stackoverflow.com/questions/24106555/memset-does-not-work-c) [[3] "Claud AI", Oct. 13, 2024 [Oct. 13, 2024]](https://claude.ai/chat/) [[4] 太雪菜. "不要再被 memset 坑了!" Aug. 21, 2022 [Oct. 16, 2024]](https://www.acwing.com/blog/content/24011/) [[5] NEKO_Daze. "[C++杂谈] memset函数为什么只能给数组初始化为0或-1?" Oct. 6, 2023 [Oct. 16, 2024]](https://www.luogu.com/article/q9p8e1bc) [[6] "ideone.com", Oct. 13, 2024 [Oct. 13, 2024]](https://ideone.com/) [[7] Sofía Huppertz, Vlad from Moscow. "What are the correct types for memset function?" May 2, 2023 [Oct. 16, 2024]](https://stackoverflow.com/questions/76154111/what-are-the-correct-types-for-memset-function)