C++ 之memset() 詳解
阿新 • • 發佈:2019-02-11
在C/C++語言中,經常需要對記憶體進行操作,裡面涉及很多函式,但是memset函式的使用需格外注意!
函式原型是:void *memset(void *s, int ch, size_t n);
函式功能是:將s所指向的某一塊記憶體中的前n個位元組的內容全部設定為ch指定的ASCII值, 第一個值為指定的記憶體地址,塊的大小由第三個引數指定,這個函式通常為新申請的記憶體做初始化工作, 其返回值為指向s的指標,它是對較大的結構體或陣列進行清零操作的一種最快方法。 標頭檔案是:<memory.h>或<string.h>
1. 基本應用場合
memset函式通常用來對一塊已經分配地址的記憶體進行初始化,並且通常初始化為0或者字元'\0'(實際上是一樣的)。下面是一些常見例子。
上面的這些例子已經比較清楚地展示了memset函式的使用,當然,很常見的還有對結構體進行這樣的初始化操作,唯一的區別就是sizeof()的物件變成結構體即可,這裡大家可以自己嘗試。
從上面這個例子中就可以看出,在memcpy這個函式中,直接使用了memset的返回值,用其來拷貝cBuf1這個字元陣列,這樣就可以直接連起來寫,看起來十分方便。但是這個例子的應用形式卻很少,只是為了說明這個問題才這樣寫的,具體的鏈式程式設計應用場合大家可以再仔細研究下,但是鏈式程式設計也使得程式碼變得有些不直觀,所以要有所取捨。 (4)最重要的一點。一定要注意,memset是按照位元組對待初始化空間進行初始化的,也就是說,函式裡面的第二個引數的那個初值(一般為0)是按照一個一個位元組往第一個引數所指區域賦值的,所以,對於單位元組資料型別(char)可以初始化為任意支援的值,都沒有問題,但是對於非多位元組資料型別只能初始化為0,而不能初始化成別的初值,因為對所有位元組按任意順序賦值0的結果都是0,而如果初始化為其他的值,就會一個位元組一個位元組的進行賦值,從而出現奇怪的結果。比如說,上面的例3之所以沒有出錯就是因為初始化為0,但是如果初始化為1,那麼因為int一般是4個位元組,那麼相當於將一個int元素初始化成了0000 0001 0000 0001 0000 0001 0000 0001,這樣對於一個int元素肯定不是1,而是一個很大的數,結果出乎意料,所以一定要記住這一點,非常重要!!! 關於此點的詳細說明如下: 剛才函式原型:
函式功能是:將s所指向的某一塊記憶體中的前n個位元組的內容全部設定為ch指定的ASCII值, 第一個值為指定的記憶體地址,塊的大小由第三個引數指定,這個函式通常為新申請的記憶體做初始化工作, 其返回值為指向s的指標,它是對較大的結構體或陣列進行清零操作的一種最快方法。 標頭檔案是:<memory.h>或<string.h>
1. 基本應用場合
memset函式通常用來對一塊已經分配地址的記憶體進行初始化,並且通常初始化為0或者字元'\0'(實際上是一樣的)。下面是一些常見例子。
/*===注意:這些例子如果同時執行需要在C++編譯環境下===*/ int i = 0; // 例1:對字元陣列進行初始化 char buf[10]; memset(buf, '\0', sizeof(char) * 10); // 0或者'\0'是等價的可檢視ASCII碼,可見下表 for (i = 0; i < 10; ++i) { printf("%c", buf[i]); } printf("\n"); // 例2:對字元指標所指區域初始化,必須已經分配記憶體 char* pBuf = (char *)malloc(sizeof(char) * 10); if (pBuf != NULL) { memset(pBuf, 0, sizeof(char) * 10); // 0或者'\0'是等價的 for (i = 0; i < 10; ++i) { printf("%c", pBuf[i]); } printf("\n"); free(pBuf); pBuf = NULL; } // 例3:對整型陣列進行初始化 int iBuf[10]; memset(iBuf, 0, sizeof(int) * 10); for (i = 0; i < 10; ++i) { printf("%d ", iBuf[i]); } printf("\n");
上面的這些例子已經比較清楚地展示了memset函式的使用,當然,很常見的還有對結構體進行這樣的初始化操作,唯一的區別就是sizeof()的物件變成結構體即可,這裡大家可以自己嘗試。
2. 需要注意的幾點
(1)memset中的第三個引數一定要使用sizeof操作符,因為每個系統下對型別長度的定義可能不一樣。 (2)memset中的第一個引數一定要是一個已知的、已經被分配記憶體的地址,否則會出錯。 (3)大家可能比較疑惑,memset的第一個引數已經有了被初始化空間的首地址,為什麼還要返回一個void*的指標去指向這個地址呢?這種結構在很多函式庫裡面比較常見,比如字串操作函式等,都有類似的現象,這裡之所以還要返回這個指標是為了實現鏈式程式設計,所謂鏈式程式設計,舉個例子大家就明白了。// 例4:鏈式程式設計
int i = 0;
char cBuf [10];
char cBuf1[10];
// 這裡是關鍵!!!
memcpy(cBuf1, memset(cBuf, 'a', sizeof(char) * 10), sizeof(char) * 10);
for (i = 0; i < 10; ++i)
{
printf("%c", cBuf[i]);
}
printf("\n");
for (i = 0; i < 10; ++i)
{
printf("%c", cBuf1[i]);
}
printf("\n");
從上面這個例子中就可以看出,在memcpy這個函式中,直接使用了memset的返回值,用其來拷貝cBuf1這個字元陣列,這樣就可以直接連起來寫,看起來十分方便。但是這個例子的應用形式卻很少,只是為了說明這個問題才這樣寫的,具體的鏈式程式設計應用場合大家可以再仔細研究下,但是鏈式程式設計也使得程式碼變得有些不直觀,所以要有所取捨。 (4)最重要的一點。一定要注意,memset是按照位元組對待初始化空間進行初始化的,也就是說,函式裡面的第二個引數的那個初值(一般為0)是按照一個一個位元組往第一個引數所指區域賦值的,所以,對於單位元組資料型別(char)可以初始化為任意支援的值,都沒有問題,但是對於非多位元組資料型別只能初始化為0,而不能初始化成別的初值,因為對所有位元組按任意順序賦值0的結果都是0,而如果初始化為其他的值,就會一個位元組一個位元組的進行賦值,從而出現奇怪的結果。比如說,上面的例3之所以沒有出錯就是因為初始化為0,但是如果初始化為1,那麼因為int一般是4個位元組,那麼相當於將一個int元素初始化成了0000 0001 0000 0001 0000 0001 0000 0001,這樣對於一個int元素肯定不是1,而是一個很大的數,結果出乎意料,所以一定要記住這一點,非常重要!!! 關於此點的詳細說明如下: 剛才函式原型:
void * memset ( void * ptr, int value, size_t num );
為地址ptr開始的num個位元組賦值value,注意:是逐個位元組賦值,ptr開始的num個位元組中的每個位元組都賦值為value。
(1) 若ptr指向char型地址,value可為任意字元值;
(2) 若ptr指向非char型,如int型地址,要想賦值正確,value的值只能是-1或0,因為-1和0轉化成二進位制後每一位都是一樣的,設int型佔4個位元組,則-1=0XFFFFFFFF, 0=0X00000000。
例,正確賦值:
int A[2];
memset(A, -1, sizeof A);
賦值過程如下圖:
由於int型佔四個位元組,memset的賦值方式是逐個位元組賦值,因此A[0]或A[1]實際上是四個位元組合在一起的值,即0XFFFFFFFF=-1。
例,錯誤賦值:
int A[2];
memset(A, 1, sizeof A);
賦值過程如下圖:
- void * memset ( void * ptr, int value, size_t num );
(1) 若ptr指向char型地址,value可為任意字元值;
(2) 若ptr指向非char型,如int型地址,要想賦值正確,value的值只能是-1或0,因為-1和0轉化成二進位制後每一位都是一樣的,設int型佔4個位元組,則-1=0XFFFFFFFF, 0=0X00000000。
例,正確賦值:
- int A[2];
- memset(A, -1, sizeof A);
賦值過程如下圖:
由於int型佔四個位元組,memset的賦值方式是逐個位元組賦值,因此A[0]或A[1]實際上是四個位元組合在一起的值,即0XFFFFFFFF=-1。
例,錯誤賦值:
- int A[2];
- memset(A, 1, sizeof A);