1. 程式人生 > >memset()函式初始化問題

memset()函式初始化問題

以上程式碼執行後,dp陣列的內容為 00000001 00000001 00000001 00000001 轉化為十進位制後不為1


  我們在很多程式中都會看到memset(a,127,sizeof(a));這樣的程式碼,127是什麼特別的數字呢?通過基礎的進位制轉換可以得知127的二進位制表示是01111111,那麼在dp數組裡放的內容就是“01111111 01111111 01111111 01111111”,(10進位制的2139062143),這樣就實現了將數組裡的全部元素初始化為一個很大的數的目的了,在最短路徑問題以及其他很多演算法中都是需要用到的。值得注意的是,int型別的範圍為2^31-1,大約是2147483647的樣子(如果我沒有記錯的話),所以初始化int型別的陣列也可以使用127這個數值。

如果是128呢?因為128的二進位制是10000000,那麼放的內容就是10000000 10000000 10000000 10000000,經過計算可得這個數是-2139062144。這樣就可以將陣列初始化為一個很小的數了。

memset的正規用法是隻能用來初始化char型別的陣列的,也就是說,它只接受0x00-0xFF的賦值。
因為char是1位元組,memset是按照位元組賦值的,相當於把每個位元組都設為那個數,所以char型的陣列可賦任意值;
而對於也常用的int型別,int是4個位元組,當memset(,1,sizeof());時,1相當於ASSCII碼的1,1轉為二進位制00000001,當做一位元組,一位元組8位,int為4位元組,所以初始化完每個數為00000001000000010000000100000001 = 16843009;

   memset(,0xff,sizeof()),0xff轉為二進位制11111111,int為4位元組所以最後為11111111111111111111111111111111為-1。(化為二進位制補位,然後再賦值)。
  可以全賦值為0,0的二進位制位000000000000000000000000000000000,還可以是-1,-1的二進位制就是11111111111111111111111111111111,所以memset可以直接初始化(0,-1);
例如:0xff轉為二進位制位11111111,正好是一位,0x1f小於0xff,而0x59也小於0xff,所以這些都可以用來初始化,只要能填滿8位的二進位制,就可以了。
如果你想初始最大化,第一位為符號位,不能為1,剩下全是1,也就是7個1,1111111化為十六進位制正好為0x7f,所以memset(,0x7f,sizeof());就可以了


Memset中無窮大常量的設定技巧
如果問題中各資料的範圍明確,那麼無窮大的設定不是問題,在不明確的情況下,很多程式設計師都取0x7fffffff作為無窮大,因為這是32-bit int的最大值。如果這個無窮大隻用於一般的比較(比如求最小值時min變數的初值),那麼0x7fffffff確實是一個完美的選擇,但是在更多的情況下,0x7fffffff並不是一個好的選擇。
很多時候我們並不只是單純拿無窮大來作比較,而是會運算後再做比較,例如在大部分最短路徑演算法中都會使用的鬆弛操作:
  if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
我們知道如果u,v之間沒有邊,那麼w[u][v]=INF,如果我們的INF取0x7fffffff,那麼d[u]+w[u][v]會溢位而變成負數,我們的鬆弛操作便出錯了,更一般的說,0x7fffffff不能滿足“無窮大加一個有窮的數依然是無窮大”,它變成了一個很小的負數。
除了要滿足加上一個常數依然是無窮大之外,我們的常量還應該滿足“無窮大加無窮大依然是無窮大”,至少兩個無窮大相加不應該出現災難性的錯誤,這一點上0x7fffffff依然不能滿足我們。
所以我們需要一個更好的傢伙來頂替0x7fffffff,最嚴謹的辦法當然是對無窮大進行特別處理而不是找一個很大很大的常量來代替它(或者說模擬它),但是這樣會讓我們的程式設計過程變得很麻煩。在我讀過的程式碼中,最精巧的無窮大常量取值是0x3f3f3f3f,我不知道是誰最先開始使用這個精妙的常量來做無窮大,不過我的確是從一位不認識的ACMer(ID:Staginner)的部落格上學到的,他/她的很多程式碼中都使用了這個常量,於是我自己也嘗試了一下,發現非常好用,而當我對這個常量做更深入的分析時,就發現它真的是非常精巧了。


    0x3f3f3f3f的十進位制是1061109567,也就是10^9級別的(和0x7fffffff一個數量級),而一般場合下的資料都是小於10^9的,所以它可以作為無窮大使用而不致出現數據大於無窮大的情形。
另一方面,由於一般的資料都不會大於10^9,所以當我們把無窮大加上一個資料時,它並不會溢位(這就滿足了“無窮大加一個有窮的數依然是無窮大”),事實上0x3f3f3f3f+0x3f3f3f3f=2122219134,這非常大但卻沒有超過32-bit int的表示範圍,所以0x3f3f3f3f還滿足了我們“無窮大加無窮大還是無窮大”的需求。
  最後,0x3f3f3f3f還能給我們帶來一個意想不到的額外好處:如果我們想要將某個陣列清零,我們通常會使用memset(a,0,sizeof(a))這樣的程式碼來實現(方便而高效),但是當我們想將某個陣列全部賦值為無窮大時(例如解決圖論問題時鄰接矩陣的初始化),就不能使用memset函式而得自己寫迴圈了(寫這些不重要的程式碼真的很痛苦),我們知道這是因為memset是按位元組操作的,它能夠對陣列清零是因為0的每個位元組都是0,現在好了,如果我們將無窮大設為0x3f3f3f3f,那麼奇蹟就發生了,0x3f3f3f3f的每個位元組都是0x3f!所以要把一段記憶體全部置為無窮大,我們只需要memset(a,0x3f,sizeof(a))。
   所以在通常的場合下,0x3f3f3f3f真的是一個非常棒的選擇。


其他賦值:
memset(arr,0x7F,sizeof(arr)); //它將arr中的值全部賦為2139062143,這是用memset對int賦值所能達到的最大值
類似的還有:
memset(arr,0x80,sizeof(arr)); //set int to -2139062144
memset(arr,0x7F,sizeof(arr)); //set double to 1.38242e+306