因為使用C++寫小題目時經常需要清除數組,這里記錄下Memset函數的sizeof運算符的使用注意。
memset的特點是:將給定地址后連續的內存(包括給定地址),逐個byte初始化為參數中指明的值。
因為是逐byte初始化,所以memset一般只用來清空(賦值為0)
如果不賦值為0,結果是什么呢?假設對於int A[],如果使用memset(A, 1, sizeof(A)),那么數組A的每一個值會被初始化為0x01010101
正因為如此,一般只會出現 memset(A, 0, sizeof(A))
對於堆區分配的數組,int *A = new int[N], memset(A, 0, sizeof(A[0])*N) 也可以達到效果,但要注意,這里第三個參數不能使用sizeof(A),而要指明byte數。
要想知道原因,需要知道sizeof 運算符的作用。它返回的是“占用的棧空間字節數”。如果數組用int A[N]的形式申明,那么sizeof(A)返回的是整個A數組的占用byte數。如果用int *A = new int[N],sizeof(A)返回的依舊是一個int *所占用的byte數,也就是說,32位編譯器會返回 4,64位編譯器會返回8。需要注意的是,對於兩種編譯器,sizeof作用在int *才會有區別,而sizeof(int) 或者 sizeof(a) (a是一個申明為int的參數) 在兩種編譯器上都返回4。
下面是一個測試程序。編譯環境為gcc 4.8.2 64-bit
int *B; int b; cout << "sizeof B: " << sizeof(B) << endl; //8,因為我的編譯器是64-bit cout << "sizeof b: " << sizeof(b) << endl; //4,無論32-bit還是64-bit編譯器,int所占字節數都是4 B = new int[5]; B[4] = 7; B[3] = 6; B[2] = 5; B[1] = 3; B[0] = 1; cout << "-------B---------\n"; for(i = 0; i < 5; ++i) cout << B[i] << ' '; //1 3 5 6 7 cout << endl; memset(B, 0, sizeof(B)); for(i = 0; i < 5; ++i) cout << B[i] << ' '; // 0 0 5 6 7 因為B 占了8字節,所以前8 byte置0 cout << endl; int C[3]; C[2] = 6; C[1] = 4; C[0] = 2; cout << "\n--------C--------\n"; cout << "size of C: " << sizeof(C) << endl; //12,返回數組所占用的字節數 for(i = 0; i < 3; ++i) cout << C[i] << ' '; //2 4 6 cout << endl; memset(C, 0, sizeof(C)); for(i = 0; i < 3; ++i) cout << C[i] << ' '; //0 0 0 cout << endl;
知道了上面的原理,我們來看看二維數組的初始化。
int A[2][3] 對於這樣的定義方式,我們依然可以通過memset(A, 0, sizeof(A))來完成對整個二維數組的初始化,因為這種方式申明的二維數組本質上還是一維排列。
如果是int **A,然后動態申明一個2*3的二維數組呢?我們可以像上面一樣,通過 memset(A, 0, sizeof(A[0][0])*2*3) 來完成二維數組的初始化嗎?
答案自然是不行。
動態分配的一維數組之所以依然能用Memset(雖然第三參數要指明byte數)來完成賦值,因為在堆區,一維數組的成員依然是連續排列。
而動態分配的二維數組中的int成員在堆區還是連續排列的嗎?當然不是,既然不是,memset就不能得到想要的結果了。那實際結果是啥?memset會清空存放二級指針的連續內存,附帶清除些別的內存區域。之后只要訪問諸如A[0][1]之類的都會引發內存錯誤,因為二維數組的結構已經被破壞。
高維數組在初始化時的注意類似於二維數組,這里就不再贅述了。