第四章的語句沒有什麼特別的東西,都是if,while,for,等正常使用的語句,唯一需要注意的是,switch語句中加default語句形成好習慣,之後的學習中會提到使用函數指針來形成轉接表代替某些switch操作。
goto語句不提倡使用雖然可以從最內層的循環直接跳出,但是很危險,容易產生依賴,跳出內層循環可以以如下的方式進行:
enum{exit,ok} status;
status = ok;
while( status == ok && condition1){
while( status == ok && condition2) {
while(condition3){
if(some disaster)
{
status = exit;//或者直接用return
break;
}
}
}
}
第五章操作符和表達式
1、移位操作符
左移對於無符號數和有符號數來說毫無爭議,後面都用0填充,
右移對於無符號數是邏輯移位,左邊都填入0,對於有符號數來說是算術移位還是邏輯移位取決於編譯器,所以不具備可移植性。
左移-5類似於下邊的式子
a << -5
這是未定義的,由編譯器決定,有些機子上面會執行左移27位的操作。
2、位操作符
可以實現對一個數據的某一位置0或者1,或者測試其是否爲1,
簡單的例子來看:可以通過判斷最後一位是否爲1來判斷是否爲偶數。
3、賦值
這裏比較重要的就是考慮如下2個式子
a[2*(y-f(x))] = a[2*(y-f(x))] + 1;
a[2*(y-f(x))] += 1
第一個式子計算了2次下標,如果f(x)有副作用,會改變x或者y的話,那麼可想而知,第一個式子a[b] = a[c]+1了,由於計算了2次下表,效率還低下。
第二個式子只計算了一次下標,不會出現問題。
4、單目操作符
第一個要注意的是例如sizeof(a = b+1),是不會對表達式進行計算的,也就是說a是不會改變,通過判斷括號裏的類型,編譯器就會輸出一個類型大小。
++號:不能使用++a = 10這樣的操作,++a的結果是a值的拷貝,不是變量本身,無法對值進行賦值
5、逗號表達式
逗號表達式是將多個表達式分隔開,從左到右求值,整個表達式的值就是最後那個表達式的值
非常經典的例子如下
a = get_value();
count_value(a);
while(a > 0){
...
a = get_value();
count_value;
}
在循環體開始前和結束的時候,有相同的語句,就可以轉移近while的條件判斷語句裏,用逗號表達式分開。while(a = get_value(),count_value(a),a>0)
6、表達式求值
C語言的整形算術運算至少以缺省的整型類型的精度來進行的,比如2個char相加,先提升爲整型計算完之後再進行階段,因爲如果存在求補或者左移的操作,8位精度是不夠的,標準要求進行完整的整型求值,這樣就不會存在歧義性。
再看下面的代碼
int a = 5000;
int b = 25;
long c = a*b;
在16位系統上,c就會錯誤,a*b就會溢出,32位不會產生問題,所以需要強制類型轉換,long c = (long)a*b;
7、優先級順序
比如
main()
{
int i = 10;
i = i-- - --i*(i = -3) * i++ + ++i;
printf("i = %d\n",i);
}
我們不知道-- 在-之後還是之前進行或者++i在什麼時候進行,所以存在歧義,不同的編譯器都產生不一樣的結果。也如c+ --c結果不一定。
如果一個連續表達式如if(x+y+1>0)存在溢出,那麼x+y先溢出還是y+1先溢出,結果不一樣,所以要分語句來執行。
像f()+h()+g(),不管先執行哪個函數,順序不一樣,結果就不同,就得單獨語句執行:
temp = f();
temp += h();
temp += g();
簡單的習題:編寫函數 unsigned int reverse_bit(unsigned int value)
這個函數的返回值是把value的二進制位模式從左到右變化一下後的值,例如在32位機器上,25這個值包含下列各個位:
00000000000000000000000000011001.
程序如下:
int answer = 0;
for(i = 1;i > 0;i <<=1)
{
answer << 1;
if(value & 1 == 1)
answer |=1;
value >>=1;
}