一般的,遊戲場景中物件的位置資訊都是用兩個 int 來描述,像這樣:
struct _pos pos;
使用的時候這樣:
// 定義乙個座標
pos pos = ;
// 獲取x軸座標
int x = pos.x;
// 獲取y軸座標
int y = pos.y;
然後我看到sdlpal中只用了乙個無符號整形來描述,具體**是這樣的:
// 來自palcommon.h
typedef dword pal_pos; // dword其實就是uint32_t
#define pal_xy(x, y) (pal_pos)(((((word)(y)) << 16) & 0xffff0000) | (((word)(x)) & 0xffff))
#define pal_x(xy) (short)((xy) & 0xffff)
#define pal_y(xy) (short)(((xy) >> 16) & 0xffff)
#define pal_xy_offset(xy, x, y) (pal_pos)(((((int)(y) << 16) & 0xffff0000) + ((xy) & 0xffff0000)) | (((int)(x) & 0xffff) + ((xy) & 0xffff)))
使用的時候這樣:
// 定義乙個座標
pal_pos pos = pal_xy(10, 10);
// 獲取x軸座標
short x = pal_x(pos); // short就是short
// 獲取y軸座標
short y = pal_y(pos);
從上面可以看出,第二種不夠第一種形象,可能還會難以理解。兩種方法都有好有壞,第一種好理解但是佔記憶體,第二種不夠形象但是節省記憶體。拿int
舉例,假如int
佔4個位元組,第一種就要佔8個位元組了,而第二種只佔4個位元組。下面解釋下第二種方法的原理。
乙個int
如何表示乙個點的座標呢?顯示器的解析度一般是1920 * 1080,小一點或大一點的都有,我們可以定義任意乙個座標([0, 1920], [0, 1080])
,超過這個範圍的座標就無法顯示到螢幕上了。乙個int
佔32位,可存放的最大數是2^31 - 1
。我們的螢幕遠沒有這麼大,就算用兩個位元組16位(最大可存放65535這個數)也是夠用的。因此我們可以將乙個int
「分成」兩部分用,分別存放x、y軸的座標值。如何將兩個整數合併成乙個整數,然後還可以把這個整數還原成兩個整數?四則運算可以將兩個整數變成乙個數,但是還原成原來的兩個數就不好辦了。但是我們可以通過位運算達到目的,巨集pal_xy(x, y)
作用是將兩個整數合成乙個,pal_x(xy)/pal_y(xy)
的作用是獲取原來的x、y值。
下面這行**要用文字說明感覺挺麻煩的,直接舉個栗子吧:將(2, 3)合成乙個數,這裡只用8位和0xf0/0xf來舉例,你用32位和0xffff0000/0xffff也是一樣。
十進位制3轉換為二進位制->
0000
0011 左移4位->
0011
0000 同0xf0進行'與運算 &'的目的是將右邊4位變成0,這裡已經是0了。
十進位制2轉換成二進位制->
0000
0010 同0xf進行'與運算 &'的目的是將左邊4位變成0,右邊4位不會變,所以無變化。
最後將 0011
0000 和 0000
0010 進行'或運算 |'得到 0011
0010。轉換成十進位制就是50,這無關緊要。
注意這裡是將y左移了,所以y是存放到了左邊兩個位元組裡,而x是存放到了右邊兩個位元組(假設int佔4個位元組)
#define pal_xy(x, y) (pal_pos)(((((word)(y)) << 16) & 0xffff0000) | (((word)(x)) & 0xffff))
要從乙個數獲取原來的兩個數,反其道行之就行了。x是存放在右邊2個位元組裡的,所以直接』與』上乙個0xffff(把左邊2個位元組的位都置為0)就可以得到了。獲取y呢,可以直接按**裡的-將這個數右移16位,再』與』上0xffff就可以了,當然還可以先』與』上0xffff0000,再右移16位。
#define pal_x(xy) (short)((xy) & 0xffff)
#define pal_y(xy) (short)(((xy) >> 16) & 0xffff)
解釋下下面這個巨集,x、y是相對於點xy的偏移量,需要注意的是:x、y並不是乙個點的座標,xy這個點也不一定是由x、y合成的。該巨集的作用是算出相對於點xy在x軸方向偏移x個距離、y軸方向偏移y個距離後的點的座標,這個座標同樣存放在pal_pos
這種型別的變數裡。原理和普通座標的計算一樣:原點(x, y),偏移量a、b,偏移後的點(x + a, y + b)。不過這裡需要用到位運算。具體是這樣的:本來十進位制的y是存放在乙個完整的4位元組裡,通過這句(int)(y) << 16) & 0xffff0000
把它存放到了左邊兩個位元組裡,並且右邊兩個位元組16位都為0,原理和上面講的一樣。這句(xy) & 0xffff0000
是將點xy的右邊兩個位元組16位置為0,這個就相當於y,而前一句獲得的那個相當於b,相加就是y + b了(這裡的兩個轉換是為了對應的上,不然直接相加是得不到正確結果的)。這句(int)(x) & 0xffff
是將原本存放在4個位元組裡的x存放到右邊的兩個位元組裡,左邊兩個位元組16位都置為0了。相對應的(xy) & 0xffff
這句將點xy的左邊兩個位元組16位置為0,這個相當於x,然後相加就是x + a了。最後將得到的兩個數『或 |』運算一下就合成乙個pal_pos
型別的點座標了。直接加也行的,但是『+』的效率比『|』的效率低。
#define pal_xy_offset(xy, x, y) (pal_pos)(((((int)(y) << 16) & 0xffff0000) + ((xy) & 0xffff0000)) | (((int)(x) & 0xffff) + ((xy) & 0xffff)))
可能上面說的有點亂,其實反過來理解也是一樣的:將點xy還原成原座標,記作(x1, y1),然後加上偏移量x、y得到點(x1 + x, y1 + y),最後將這個點轉換回去就行了。完整**就是pal_pos((pal_x(xy) + x), (pal_y(xy) + y))
。這句**只是幫助理解,可能會有問題,因為pal_x()/pal_y()
是short
型別的,x、y預設是int
型別的,強制轉換後相加的資料可能不準確,所以還是用原來的**就行了。 用乙個指向int的指標來儲存乙個物件的位址。
include using namespace std class class int main size of class 4 include using namespace std class class virtual void fun2 virtual void fun3 int main ...
用乙個指向int的指標來儲存乙個物件的位址。
include using namespace std class class int main size of class 4 include using namespace std class class virtual void fun2 virtual void fun3 int main ...
用乙個 int 表示 IP位址
用乙個 int 表示 ip位址。這還是08年初,面試華為時被問到的,當時也回答上來了。不過,最終 hr 也沒要我就是了 public class ip2integer else system.out.println iipv4 sb.tostring return sb.tostring conve...