C 和指針第 5 章 操作符和表達式 筆記

總述

這章標題爲操作符和表達式,主要講述了操作符、布爾值、左值和右值、表達式求值,下面分別簡要介紹下

操作符

算術操作符

+-*/%

這裏說下 /%,前者求商,後者用來求模也就是求餘數,例如

#include <stdio.h>

int main(void)
{
    printf("10/3=%d\n", 10/3); // 10/3=3
    printf("10%3=%d\n", 10%3); // 10%3=1
    return 0;
}
移位運算符

<<>> 也叫左移和右移,簡單點說左移幾位就是乘以 2 的幾次方,比如 1 左移 1 位,那麼二進制表示爲 0000 0010 ,轉成十進制就是 1*2^1 + 0*2^0=2,套用剛剛的方法 1 * 2^1 = 2 ;右移幾位就是除以 2 的幾次方,2 向右移 1 位,二進制表示爲 0000 0001,十進制位 1*2^0=1,套用方法 2/2^1=1

#include <stdio.h>

int main(void)
{
    printf("2<<1=%d\n", 2<<1); // 2<<1=4
    printf("2*2=%d\n", 2*2); // 2*2=4
    printf("2>>1=%d\n", 2>>1); // 2>>1=1
    printf("2/1=%d\n", 2/2); // 2/1=1
    return 0;
}

書中給了個數 1 的例子,核心就是每次判斷最低位是否爲 1

#include <stdio.h>

int count_one_bits(unsigned value);

int main(void)
{
    int value = 10;
    int count;
    
    count = count_one_bits(value);
    printf("%d have %d one bits\n",value, count);
    
    return 0;
}

int count_one_bits(unsigned value)
{
    int count = 0;
    for (;value != 0; value = value >> 1) {
        if (value % 2 != 0) {
            ++ count;
        }
    }
    return count;
}
位運算符

看下面這幅圖就夠了
在這裏插入圖片描述
這裏就可以用位運算符來優化上面數 1 的算法了

int count_one_bits(unsigned value)
{
    int count = 0;
    for (;value != 0; value = value >> 1) {
        if ((value & 1) != 0) {
            ++ count;
        }
    }
    return count;
}
賦值

通過賦值表達式對變量就行賦值,太簡單了,就不展開了。

單目運算符
  • !,對整型操作符按邏輯取反,真變假,10
  • ~,對整型操作符求補操作,操作數中的 1001
  • +-++-- 太簡單,不介紹
  • &, 取地址符
  • * ,間接訪問操作符,與指針一起使用
  • sizeof,判斷操作類型的長度,以字節數爲單位
  • (類型),強制類型轉換
關係運算符

>>=<<=!=== 看看就能明白。

邏輯運算符

&&||,這裏需要說的是 ||短路求值 特性,也就是它首先對左操作數進行求值,如果爲真,那麼右操作數便不再求值。

條件運算符

也叫三目運算符,exp1 ? exp2 : exp3,如果 exp1 爲真,那麼表達式的值就是exp2,否則就是 exp3

逗號操作符

exp1, exp2, exp3,這個表達式自左向右逐個求值,整個逗號表達式的值就是最後那個表達式的值。

if (b + 1, c / 2, d > 0)

如果 d > 0 那麼整個表達式的值就爲真,這種用法比較少見,能不用就不用吧。

下標引用、函數調用和結構成員
  • 下標引用, array[10]
  • 函數調用, func(int a, int b)
  • 結構成員,.-> 操作符用於訪問一個結構的成員。如果 s 是個結構變量,那麼 s.a 就訪問 s中名叫 a的成員;當你擁有一個指向結構的指針而不是結構本身,且欲訪問它的成員時,就需要使用 s->a而不是 s.a

布爾值

C 不具備顯式的布爾類型,所以使用整數來代替:零時假,任何非零值皆爲真,因此代碼中可以使用宏來代替

#define TRUE 1
#define FALSE 0

左值和右值

左值 L-value 【意味着一個確定/特定的位置】
右值 R-value 【意味着一個值】
在使用右值的地方可以使用左值,但是在需要左值的地方卻不能使用右值

a = b + 25;
a 是個左值,因爲它標識了一個可以存儲結果值的地點
b + 25 是個右值,因爲它指定了一個值
b + 25 = a;
原先用作左值的 a 此時可以當做右值,因爲每個位置都包含一個值。
而 b + 25 不能作爲左值,因爲它並未標識 【一個特定的位置】。因此,這條賦值語句是非法的。
注意當計算機計算 b + 25時,它的結果必然保存於機器的某個地方。但是,程序員並沒有辦法
預測該結果會存儲在什麼地方,也無法保證這個表達式的值下次還會存儲於那個地方。其結果是,
這個表達式不是一個左值。基於同樣的理由,字面值常量也都不是左值。
聽上去似乎是變量可以作爲左值而表達式不能作爲左值,但這個推斷並不準確。在下面的賦值
語句中,左值便是一個表達式。
int a, *pi;

pi = &a;
*pi = 20;

第二條賦值語句,是個表達式語句,但卻是合法的的左值。
因爲 pi 的值是內存中某個特定位置的地址,* 操作符使機器指向那個位置

當它作爲左值使用時,這個表達式指定需要進行修改的位置
當它作爲右值使用時,它就是提取當前存儲於這個位置的值

表達式和求值

隱式類型轉換

主要見於字符的類型一般用 int,而不能用 char,防止類型截短造成判斷失誤

算數轉換

主要是整型到浮點型的轉換,看下圖

在這裏插入圖片描述

操作符的屬性

複雜表達式的求值順序是由三個因子決定:操作符的優先級、操作的結合性、操作符是否控制執行的順序。具體可以參考書中的表格

優先級和求值順序

這個和上面那個緊密相關,個人理解,對於複雜的表達式,還是多加括號聚組的好,不用勞心費力的辨別優先級了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章