# 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)