一、前言
memset 作為對內存初始化的函數,還是有不少坑和誤區的,今天就來對這個函數作一個總結。避免后期使用不當踩入坑。
二、函數作用
最簡單的調用就是將一個數組清零,代碼如下:
const int maxn = 1024;
int a[maxn];
memset(a, 0, sizeof(a)); // 結果:a[0]=a[1]=a[...]=0;
- 這里 sizeof(a) = maxn * 4 = 4096;
- 表示的是將 數組首地址 a 開始往后的 4096 個字節,都設置為 0;
三、效率對比
直接調用 memset 接口清零 和 調用循環進行清零,進行一個測試后如下:
對長度為 10000000 的數組,執行100次調用;
模式 | memset | for |
---|---|---|
debug | 375ms | 2156ms |
release | 343ms | 329ms |
- 因為 release 版本會做各種優化,編譯器發現重復執行無效邏輯就會跳過,所以不太好造數據測試,研究時間效率的時候還是參考 debug 版本(當然,軟件發布的時候肯定用的是 release 版本)。
- memset 無論從時間效率,還是代碼整潔來看都是由於 for 循環的,當然也帶來了一些容易引起誤解的地方。
四、誤區總結
1、按字節設置
- memset 實現原理是根據字節來設置的,比如對於字節數組char a[100],將所有字節都設置為5,就可以調用:
memset(a, 5, sizeof(a));
- 但是,對於int b[100],也采用這種方法,就會導致錯誤:
memset(b, 5, sizeof(b));
- 得到 b 數組中元素的值為 84215045;
- 為什么呢?
- 我們把這個數組轉換成二進制,得到:
- ( 00000101 00000101 00000101 00000101 ) 2 (00000101 \ 0000 0101 \ 0000 0101 \ 0000 0101)_2 (00000101 00000101 00000101 00000101)2
- 因為 i n t int int 占據了 4 4 4 個字節,把每個字節都設置成了5,所以最后轉成十進制就變成了 84215045;
- 同理,當類型是 short(二字節整數),或者 long long(八字節整數)都會有類似問題,總結表格如下:
memset值 | char | short | int | long long |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
-1 | -1 | -1 | -1 | -1 |
5 | 5 | 1285 | 84215045 | 361700864190383365 |
- 表格中,只有0 和 -1是正常的,因為 0 的二進制表示中,所有位都為0;-1 的二進制表示中,所有位都為 1;
- 特別的,當需要設置的數,對應類型的每個字節都是同一個數的時候,也可以采用 memset,比如:int 類型的 252645135(十六進制表示為:0x0f0f0f0f);
2、設置的值只有最低字節有效
memset(a, 0x05ffffff, sizeof(a));
memset(a, 0xffffff05, sizeof(a));
memset(a, 0xffffff08, sizeof(a));
memset(a, 0x12345678, sizeof(a));
- 設置值的時候,只會采用最低的字節作為賦值用,通俗的講,就是以上四句話調用,等價於:
memset(a, 0xff, sizeof(a));
memset(a, 0x05, sizeof(a));
memset(a, 0x08, sizeof(a));
memset(a, 0x78, sizeof(a));
3、堆內存不可直接 sizeof 取首地址
在堆上申請了一個數組空間,並且想要給它初始化,調用如下:
const int maxn = 1024;
int *p = new [maxn];
memset(p, 0, sizeof(p));
- 這里進入了另一個誤區,因為 p p p 在這里雖然是數組首地址,但是它扮演的角色更多的,其實是個指針,所以在進行 sizeof 運算符操作的時候,取得的值並不是 4096,而是指針的大小;
- 32位機子上,指針大小為4,;64位機子上,指針大小為 8;
- 正確做法是:
const int maxn = 1024;
int *p = new [maxn];
memset(p, 0, maxn * sizeof(int));
4、傳參數組不可直接 sizeof 取首地址
- 對傳參為數組的數據進行 memset,調用如下:
void fun(int a[maxn])
{
memset(a, 0, sizeof(a));
}
- 這里調用同樣是錯誤的,因為當數組作為傳參的時候,這里的 a 已經退化為指針,所以同樣不能用 sizeof 數組首地址來取大小;
- 正確做法是:
void fun(int a[maxn])
{
memset(a, 0, maxn * sizeof(int));
}
- 當然,當傳參是結構體指針的時候也是如此;
參考於:CSDN- 英雄哪里出來https://blog.csdn.net/WhereIsHeroFrom/article/details/111660632