本章問題
1.這個表達式的類型和值為多少?
(float)(25/10)
answer:The cast is applied to the result of the division,and because both operand are integers,a truncating(截斷) integer division is done,The value therefore is 2.0,If you wanted to do a floating-point divisioin,use this expression instead (float)25/10,the same applies to other integer expressions.
(由於兩個操作數都是整數,因此除法完成的時候得到一個被截斷的整數再轉化為浮點數,因此值為2.0,如果你想要做浮點數除法,那么應該用表達式 (float)25 / 10,這也適應其他整型表達式)
2.下面這個程序的結果是什么?
int func (void) { static int counter = 1; return ++counter; } int main() { int answer; answer = func() - func() * func(); printf("%d\n",answer); }
answer:Trick question! The obvious answer is -10(2 - 3 * 4),but in fact it is implemnentation dependent,The multiplication must be completed before the addition,but there isn't a rule mat determines the order in which the function calls are done,Thus,the answer could be any of the following:
(一個狡猾的問題,顯然的結果是-10,不過事實上根據編譯器的不同而不同,乘法一定先於加法計算,不過函數計算的先后順序並沒有規定,所以下面答案都有可能)
-10 (2 - 3 * 4) or (2 - 4 * 3) -5 (3 - 2 * 4) or (3 - 4 * 2) -2 (4 - 2 * 3) or (4 - 3 * 2)
3.你認為位操作符和移位操作符可以用在什么地方?
answer:They are often used when programming device controllers to set or test specific bits in specific locations,If anyone comes up with other good answers,please mail them to me at kar@cs.rit.edu!
(它們經常被使用於當程序設計控制設計或測試特殊的位在特殊的地方,如果任何人發現其他更好的答案,請把答案發郵箱給我kar@cs.rit.edu)
4.條件操作符在運行時較之if語句是更快還是更慢?試比較下面兩組代碼:
//left: if(a > 3) i = b + 1; else i = c * 5; //right: i = a > 3 ? b + 1 : c * 5;
answer:Not they each do precisely(精確的) the same work,If you want to get picky(挑剔),the if version might possibly be a tad(一點兒) longer because it has two instructions to store into i.However,only one of them will be executed,so there isn't any difference in speed.
(不,它們同樣的工作,如果你想要挑剔,if語句可能要長一點因為它有兩條指令來存儲i,然而只有一條語句將被執行,所以它們在速度上沒有區別)
5.可以被4整除的年份是閏年,但是其中能夠被100整除的又不是閏年,不過,其中能被400整除的又是閏年,請用一條賦值語句,如果變量year的值是閏年,把leap_year的值設置為真,如果year不是閏年,把leap_year設置為假。
answer:
leap_year = (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
6.哪些操作符具有副作用,它們具有什么副作用?
answer:The () operator doesn't have any side effects,but the function being called might.
(括號操作符沒有任何副作用,不過當函數被調用的時候可能有副作用)
Operator | Side Effect |
++ , -- | In both prefix and postfix forms,these operators modify the L-value on which they operate. (前綴和后綴形式都會修改它們的左值操作數) |
= | And all of the other assignment operators:They all modify the L-value given as the left operand. (所有的復制操作符,都會修改它們的左值操作數) |
7.下面這個代碼段的結果是:
int a = 20; ... if(1 <= a <= 10) printf("In Range\n"); else printf("Out of Range\n");
answer:it print In Range,This is because 1 <= a is true and evaluates to 1;The expression then tests 1 <= 10,which is also true.
(將打印出In Range,這是因為1 <= a是真的並且等於1,這個表達式之后測試 1 <= 10也是對的)
8.改寫下面的代碼段,消除多余的代碼
a = f1(x); b = f2(x + a); for(c = f3(a,b); c > 0; c = f3(a,b)){ statements a = f1(++x); b = f2(x + a); }
answer:
while(a = f1(x),b = f2(x + a),c = f3(a,b),c > 0){ statement ++x; }
9.下面的循環能實現它的目的嗎?
non_zero = 0; for(i = 0; i < ARRAY_SIZE; i += 1) non_zero += array[i]; if(!non_zero) printf("Values are all zero\n"); else printf("there are nonzero values\n");
answer:No,It fails if the array contains nonzero values that happen to sum to zero.
(不能,當數組包括不為零的值但是它們之和為零,這種情況就是錯的)
10.根據下面的變量聲明和初始化,計算下列每個表達式的值,如果某個表達式具有副作用,注明它們,在計算每個表達式時,每個變量所使用的是開始時給出的初始值,而不是前一個表達式的結果。
int a = 10, b = -25; int c = 0; d = 3; int e = 20; a. b b. b++ c. --a d. a/6 e. a % 6 f. b % 10 g. a << 2 h. b >> 3 i. a > b j. b = a k. b == a l. a & b m. a ^ b n. a | b o. ~b p. c && a q. c || a r. b ? a : c s. a += 2 t. b &= 20 u. b >>= 3 v. a %= 6 w. d = a > b x. a = b = c = d y. e = d +( c = a + b) + c z. a + b * 3 aa. b >> a - 4 bb. a != b != c cc. a == b == c dd. d < a < e ee. e > a > d ff. a - 10 > b + 10 gg. a & 0x1 == b & 0x1 hh. a | b << a & b ii. a > c || ++a > b jj. a > c && ++a > b kk. !~b++ ll. b++ & a <= 30 mm. a - b, c += d, e - c nn. a <<= 3 > 0 oo. a <<= d > 20 ? b && c++ : d--
answer:
a. -25 b. -25, b = -24 c. 9, a = 9 d. 1 e. 4 f. -5 g. 40 h. -4 i. 1 j. 10, b = 10 k. 0 l. 2 m. -19 n. -17 o. 24 p. 0 q. 1 r. 10 s. 12, a = 12 t. 4, b = 4 u. -4, b = -4 v. 4, a = 4 w. 1, d = 1 x. 3, a = 3, b = 3, c = 3 y. -27, c = -15, e = -27 z. -65 aa. -1 bb. 1 cc. 1 dd. 1 ee. 0 ff. 1 gg. 0 hh. -25590 ii. 1 jj. 1, a = 11 kk. 0, b = -24 ll. 1, b = -24 mm. 17, c = 3 nn. 20 oo. 80, a = 80, d = 2
11.下面列出幾個表達式,請判斷編譯器是如何對各個表達式進行求值的,並在不改變求值順序的情況下,盡可能去除多余的括號。
a. a+(b/c) b. (a+b)/c c. (a*b)%6 d. a*(b%6) e. (a + b) == 6 f. ! (( a >= ’0’) && (a <= ’9’) ) g. (( a & 0x2f ) == ( b | 1 )) && ((~ c) > 0) h. (( a << b ) – 3 )<( b <<( a + 3)) i. ~ (a ++) j. ( (a == 2) || (a == 4) ) && ( (b == 2) || (b == 4) ) k. (a&b)^(a|b) l. (a+(b+c))
answer:
a. a+b/c b. (a+b)/c c. a*b%6 d. a*(b%6) e. a + b == 6 f. ! ( a >= ’0’ && a <= ’9’ ) g. ( a & 0x2f ) == ( b | 1 ) && ~ c > 0 h. ( a << b ) – 3 < b << a + 3 i. ~ a ++ j. ( a == 2 || a == 4 ) && ( b == 2 || b == 4 ) k. a&b^(a|b) l. a+(b+c)
12.如何判斷在你的機器上對一個有符號值進行右移位操作時執行的是算數移位還是邏輯移位?
answer:On a two's complement(補碼) machine,declare a signed integer,assign it a negative value,right shift it one bit,and then print the result,If it is negative,an arithmetic shift was used;positive indicates a logical shift.
(聲明一個有符號數,賦值一個負數,右移一位后結果如果是負數則為算數移位,否則為邏輯右移)
本章練習
1.編寫一個程序,從標准輸入讀取字符,並把它們寫到標准輸出中。除了大寫字母字符要轉換為小寫字母之外,所有字符的輸出形式應該和他的輸入形式完全相同。
answer:
#include <stdio.h> int main() { int ch; while((ch = getchar()) != EOF){ if(ch >= 'A' && ch <= 'Z') ch -= ('A' - 'a'); putchar(ch); } return 0; } /*以上代碼只能在以ASCII碼運行的機器上,在其他字母不是連續編碼的系統中不具備可移植性*/ /*下面代碼是采用庫函數來完成轉換,注意tolower本身並不會改變字符的值,返回值會輸出小寫字符*/ #include <stdio.h> #include <ctype.h> int main() { int ch; while((ch = getchar()) != EOF){ putchar(tolower(ch)); } return 0; }
2.編寫一個程序,從標准輸入讀取字符,並把它們寫到標准輸出中,所有非字母字符都完全按照它的輸入形式輸出,字母字符在輸出之前進行加密。
加密的方法很簡單:每個字母被修改為在字母表上距其13個位置(前或后)的字母,例如,A被修改為N,B被修改為O,Z被修改為M,以此類推,注意大小寫字母都應該被轉換。提示:記住字符實際上是一個較小的整型這一點可能會對你有所幫助。
answer:
#include <stdio.h> int main() { int ch; while((ch = getchar()) != EOF){ if((ch >= 'A' && ch <= 'M') || (ch >= 'a' && ch <= 'm')) ch += 13; else if((ch >= 'N' && ch <= 'Z') || (ch >= 'n' && ch <= 'z')) ch -= 13; putchar(ch); } return 0; }
3.請編寫函數
unsigned int reverse_bits(unsigned int value);
這個函數的返回值是吧value的二進制位模式從左到右變換一下之后的值。例如,在32位機器上,25這個值包含下列各位:
00000000,00000000,00000000,00011001
函數的返回值應該是2550136832,它的二進制位模式是:
10011000,00000000,00000000,00000000
編寫函數時注意不要讓它依賴於你的機器上整型值的長度。
answer:
unsigned int reverse_bits(unsigned int value) { unsigned int dst = 0; int i; for(i = 1; i != 0; i <<= 1){ dst <<= 1; if(value & 1) dst |= 1; // dst <<= 1; value >>= 1; } return dst; }
注意dst <<= 1這條語句必須出現在dst |= 1之前,否則dst的最后一位不管是什么都會被左移一位,正確的答案相當於做了一次左移位運算。
4.編寫一組函數,實現位數組,函數的原型如下:
void set_bit(char bit_array[], unsigned bit_number); void clear_bit(char bit_array[], unsigned bit_number); void assign_bit(char bit_array[], unsigned bit_number, int value); int test_bit(char bit_arrar[], unsigned bit_number);
每個函數的第一個參數是字符數組,用於實際儲存所有的位,第二個參數用於標識需要訪問的位。函數的調用者必須確保這個值不要太大,以至於超出數組邊界。第一個函數把指定的位設置為1,第二個函數把指定的位清零,如果value的值為1,第三個函數就把指定的位清零,否則置一,至於最后一個函數,如果參數中指定的位不為零就返回真,否則返回假。
answer:
/*這個題目如果不仔細讀題的話,可能一下子寫出下面的代碼出來,以第一個函數為例*/ void set_bit(char bit_array[], unsigned bit_number) { if(bit_number <= sizeof(bit_array)){ bit_array[bit_number] |= 1 << bit_number; } } /*如果是這樣寫的話,那就錯得很厲害了*/
//這是正確的代碼 unsigned character_offset(unsigned bit_number) { return bit_number / CHAR_BIT; } unsigned bit_offset(unsigned bit_number) { return bit_number % CHAR_BIT; } void set_bit(char bit_array[], unsigned bit_number) { bit_array[character_offset(bit_numer)] |= 1 << bit_offset(bit_number); } void clear_bit(char bit_array[], unsigned bit_number) { bit_array[character_offset(bit_number)] &= ~ (1 << bit_offset(bit_number)); } void assign_bit(char bit_array[], unsigned bit_number, int value) { if(value != 0) set_bit(bit_array,bit_number); else clear_bit(bit_array,bit_number); } int test_bit(char bit_arrar[], unsigned bit_number) { if(bit_array[character_offset(bit_number)] & 1 << bit_offset(bit_number) != 0) return 1; else return 0; }
/*剛開始看我是不太懂的,咋整得這么麻煩呢,接下來分析一下:*/ /* 首先bit_array是一個字符數組,它里面存的是字符,假定在你的機器上每個字符的二進制位數為n,
那么bit_array的每一個元素都有n位,如果bit_array有m個元素,那么總的bit_array就有m * n個位,
如果指定在bit_number位的話,那么應該就是數組的第bit_number / n 個元素,
所以character_offset函數的作用就是將bit_number 轉換為字符數組中的元素 因為每一個元素都只有n位,而bit_number這個值是針對所有的元素位數的,
所以你需要將它定位在第bit_number這個元素的位數,
而這個位可以用取余的方法bit_number % n來得到,
所以bit_offset的作用就是把bit_number轉換到需要改變的元素的精確位 而CHAR_BIT就是機器上字符的二進制位數 */
5.編寫一個函數,把一個給定的值存儲到一個整數中指定的幾個位。它的原型如下:
int store_bit_field(int original_value, int value_to_store, unsigned starting_bit, unsigned ending_bit);
假定整數中的位是從右向左進行編號。因此,起始位的位置不會小於結束位的位置,為了更清楚的說明,函數應該返回下列值。
原始值 | 需要儲存的位 | 起始位 | 結束位 | 返回值 |
0x0 | 0x1 | 4 | 4 | 0x10 |
0xffff | 0x123 | 15 | 4 | 0x123f |
0xffff | 0x123 | 13 | 9 | 0xc7ff |
提示:把一個值存儲到一個整數中指定的幾個位分為5個步驟,以上表最后一行為例:
1).創建一個掩碼,它是一個值,其中需要存儲的位置相對應的那幾個位設置為1,此時掩碼為
00111110,00000000
2).用掩碼的反碼對原值執行AND操作,將那幾個位設置為0.原值1111111111111111,操作后變為
11000001,11111111
3).將新值左移,使它與需要存儲的位對齊,新值00000001,00100011(0x123),左移后變為
01000110,00000000
4).把移位后的值與掩碼進行位AND操作,確保除那幾個需要存儲的位之外的其余位都設置為0,進行這個操作之后,值變為
00000110,00000000
5).把結果值與原值進行位OR操作,結果為(0xc7ff)
11000111,11111111
在所有任務中,最困難的是創建掩碼,你一開始可以把~0這個值強制轉換為無符號值,然后再對它進行移位。
answer:
#include <stdio.h> int store_bit_field(int original_value, int value_to_store, unsigned starting_bit,unsigned ending_bit); int main(void) { printf("%x\n",store_bit_field(0x0,0x1,4,4)); printf("%x\n",store_bit_field(0xffff,0x123,15,4)); printf("%x\n",store_bit_field(0xffff,0x123,13,9)); return 0; } int store_bit_field(int original_value, int value_to_store, unsigned starting_bit,unsigned ending_bit) { int value; int i = ending_bit; int unmask = 0; int mask = 0; int num = starting_bit - ending_bit + 1; while(num != 0){ mask <<= 1; mask |= 1; num--; } while(i != 0){ i--; mask <<= 1; } unmask = ~mask; original_value &= unmask; i = ending_bit; while(i != 0){ i--; value_to_store <<= 1; } value = value_to_store & mask; value |= original_value; return value; }