CS:APP這本書真的可以說是計算機的內容 給你從頭講到尾了,雖然每個領域方面的深度不夠,但是已經足夠了,因爲每一個領域都不是這麼簡單就能夠說完的,這本書能把這麼多東西講得很清楚真的不容易,所以看完建議挑戰一下lab。
要求:
不允許使用條件語句和循環語句,只允許使用8個運算符:! ˜ & ˆ | + >來完成,某些題目會額外限制運算符數量,最大隻能使用8位整數。
PART1
bitAnd
只能使用~和|運算符完成&運算,運算符最多隻能操作8次。
簡單,對兩個操作數分別取反再|就是他們兩個都沒有的位,然後再取反就是他們兩個都有的位了。
int bitAnd(int x, int y) {
return ~((~x)|(~y));
}
getByte
獲得x的第n個字節,運算符最多操作6次,只能使用!~&^|+<<>>。
也很容易,首先很容易想到獲得x的第n個字節的方法是x>>(n*8)&0xff。但是不能用*號,所以改用<<運算符來代替*號。
int getByte(int x, int n) {
return x>>(n<<3)&0xff;
}
logicalShift
實現邏輯右移,只能使用~&^|+<<>>運算符,最大操作數是20。
這題頗有難度,首先我們普通的>>運算是算數右移,在這基礎上把前n位清0就可以得到邏輯右移的結果,把前n位清0等於構造32-n位的1與之做&運算,構造這個可以用2**x - 1的方法,那麼2**x又等於2<<x,-1則等於0取反。但是構造32-n和無法使用long long類型讓我頭疼,結果我還是去看了別人怎麼寫這個。。。用左移再右移的方法防止溢出的同時可以利用符號位來讓前面填滿1。這是個好技巧。
int logicalShift(int x, int n) {
return x>>n&(~(1<<31>>n<<1));
}
bitCount
計算整數裏有多少個位是1,只能使用~&^|+<<>>運算符,最大操作數40。
這題直接算的話是肯定超操作數的,那麼只能想辦法分塊然後同時操作了,我們把每8位分成一塊,塊裏面的每一位我們可以枚舉用&操作來判定,最後再把所有塊的結果加起來就完了。
int bitCount(int x) {
int mask = ((1<<8|1)<<8|1)<<8|1;
int sum = x&mask;
sum += x>>1&mask;
sum += x>>2&mask;
sum += x>>3&mask;
sum += x>>4&mask;
sum += x>>5&mask;
sum += x>>6&mask;
sum += x>>7&mask;
sum += sum>>8&0xff;
sum += sum>>16&0xff;
sum += sum>>24&0xff;
return sum&0xff;
}
bang
做!運算,只能用~&^|+<<>>,最大操作數12。
這個首先得知道幾個技巧,假設x的最低位爲1的位置是i,x&(~x)+1可以得到結果只保留第i個1的數,即結果是2的i次方,而x|(~x)+1可以得到i及他以上的位置全部爲1的數,即得到...111111100000...(i個0,總位數-i個1)。而0是沒有1的,所以0做這兩個操作結果還是0。而其他數做上述的第二個操作得到的最高位必定是1,那麼我們就可以根據最高位來判定。
int bang(int x) {
return ((x|((~x)+1))>>31)&1^1;
}
PART2
在這個部分可以使用條件和循環語句,可以使用int和unsigned類型的整數,不能使用union,struct,數組,不能使用浮點數,浮點數都以unsigned類型傳遞並返回。
tmin
返回補碼整數最小值。
int tmin(void) {
return 1<<31;
}
fitsBits
判斷是否能用n位二進制表示一個數,可以返回1,否則返回0.
這題描述不太好,怎麼纔算能用n位來表示一個數呢,他的意思是前綴相同的話是可以用一位來省略的,比如前綴全是0就可以用一個...0來表示,全是1則用...1來表示,那麼問題就轉換爲去掉後綴n位後是否全是1或者全是0,具體思路看下面代碼。
int fitsBits(int x, int n) {
int tmp = x>>(n+(~0));
return !tmp|(!(tmp+1));
}