[C 語言] 程式設計教學:如何使用運算子 (Operators)

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    在程式語言中,運算子多以符號表示,通常都無法再化約成更小的單位,所以運算子可視為該語言的基礎指令。本文介紹 C 語言的運算子。

    代數運算子

    代數運算子用來進行日常的十進位代數運算。包括以下運算子:

    • +:相加
    • -:相減
    • *:相乘
    • /:相除
    • %:取餘數

    以下是簡短範例:

    #include <assert.h>
    
    int main(void)
    {
        assert(4 + 3 == 7);
        assert(4 - 3 == 1);
        assert(4 * 3 == 12);
        assert(4 / 3 == 1);
        assert(4 % 3 == 1);
    
        return 0;
    }
    

    許多 C 語言教材會用 printf 將資料輸出終端機,但我們刻意用 assert 巨集檢查運算結果是否正確。因為透過 assert 巨集可自動檢查程式是否正確,但使用 printf 函式得手動檢查。而且 assert 敘述可以表明我們的意圖。

    除了上述運算子,還有遞增和遞減運算子:

    • ++
    • --

    遞增、遞減運算子會帶著隱性的狀態改變,在使用時要注意。以下是範例:

    #include <assert.h>
    
    int main(void)
    {
        unsigned n = 3;
    
        assert(++n == 4);
        assert(n++ == 4);
        assert(n == 5);
    
        return 0;
    }
    

    在這個範例中,第一個 assert 敘述使用 ++n,會先加 1 再比較值。但第二個 assert 敘述使用 n++,會先比較值才加 1

    由於遞增、遞減運算子會造成隱性的狀態改變,應儘量把敘述寫簡單一些,以免發生預期外的結果。

    二元運算子

    二元運算子用在二進位數運算。包括以下運算子:

    • &:bitwise AND
    • |:bitwise OR
    • ^:bitwise XOR
    • ~:取補數
    • <<:左移 (left shift)
    • >>:右移 (right shift)

    二元運算有其規則。以下是 & (bitwise AND) 的運算規則:

    pqp & q
    111
    100
    010
    000

    歸納起來,就是「兩者皆為真時才為真」。

    以下是 | (bitwise OR) 的運算規則:

    pqp | q
    111
    101
    011
    000

    歸納起來,就是「兩者任一為真即為真」。

    以下是 ^ (bitwise XOR) 的運算規則:

    pqp ^ q
    110
    101
    011
    000

    以下是 ~ (取補數) 的規則:

    p~p
    10
    01

    只看規則會覺得有點抽象,所以我們補上範例程式,並把運算的過程寫在註解裡:

    #include <assert.h>
    
    int main(void)
    {
        int a = 3;  /* 0000 0011 */
        int b = 5;  /* 0000 0101 */
        
        /*     0000 0011
            &) 0000 0101
            -------------
               0000 0001  */
        assert((a & b) == 1);
        
        /*     0000 0011
            |) 0000 0101
            -------------
               0000 0111  */
        assert((a | b) == 7);
        
        /*     0000 0011
            ^) 0000 0101
            -------------
               0000 0110  */
        assert((a ^ b) == 6);
        
        /* <<) 0000 0101
           --------------
               0000 1010  */
        assert((b << 1) == 10);
        
        /* >>) 0000 0101
           --------------
               0000 0010  */
        assert((b >> 1) == 2);
    
        return 0;
    }
    

    二進位運算比十進位運算不直觀,但速度較快。故二進位運算多用於注意速度的低階運算,平常用不太到。

    比較運算子

    比較運算子用來比較兩個值之間的相對關係。包含以下運算子:

    • ==:相等
    • !=:相異
    • >:大於
    • >=:大於或等於
    • <:小於
    • <=:小於或等於

    以下是簡短的範例程式:

    #include <assert.h>
    #include <stdbool.h>
    
    int main(void)
    {
        assert(4 == 4);
        assert(4 != 5);
        
        assert(5 > 4);
        assert(5 >= 4);
        
        assert(3 < 4);
        assert(3 <= 4);
    
        return 0;
    }
    

    要注意比較運算子只能用來比較基礎型別,而且要兩者間相容。對於其他型態的資料,要自行撰寫比較函式。像是標準函式庫中的 string.h 函式庫就有比較 C 字串的 strcmp() 函式。其範例如下:

    #include <assert.h>
    #include <string.h>
    
    int main(void)
    {
        assert(strcmp("C", "C++") < 0);
        assert(strcmp("C", "Ada") > 0);
    
        return 0;
    }
    

    邏輯運算子

    邏輯運算子用來進行布林運算。以下是 C 語言的邏輯運算子:

    • &&:logic AND
    • ||:logic OR
    • !:logic NOT

    C 語言雖然沒有真正的布林數,仍然有布林語境,故仍可進行布林運算。

    邏輯運算子和二元運算子規則類似,符號也有點類似,要注意不要寫錯。

    以下是 && (logic AND) 的運算規則:

    pqp && q
    truetruetrue
    truefalsefalse
    falsetruefalse
    falsefalsefalse

    以下是 || (logic OR) 的運算規則:

    pqp || q
    truetruetrue
    truefalsetrue
    falsetruetrue
    falsefalsefalse

    以下是 ! (logic NOT) 的運算規則:

    p!p
    truefalse
    falsetrue

    我們使用 C99 引入的 stdbool.h 函式庫做一些基礎布林運算:

    #include <assert.h>
    #include <stdbool.h>
    
    int main(void)
    {
        assert((true && true) == true);
        assert((true && false) == false);
        assert((false && true) == false);
        assert((false && false) == false);
    
        assert((true || true) == true);
        assert((true || false) == true);
        assert((false || true) == true);
        assert((false || false) == false);
    
        assert((!true) == false);
        assert((!false) == true);
    
        return 0;
    }
    

    要注意 truefalse 都是巨集宣告,這個範例不代表 C 語言有布林數。

    指派運算子

    指派運算子分為一般指派運算和複合指派運算。一般指派運算就是單純的賦值。複合指派運算則是小小的語法糖。C 語言包含以下指派運算子:

    • =:一般賦值
    • +=:相加後賦值
    • -=:相減後賦值
    • *=:相乘後賦值
    • /=:相除後賦值
    • %=:取餘數後賦值

    以下是簡短的範例:

    #include <assert.h>
    
    int main(void)
    {
        unsigned short n = 4;
    
        n += 3;
        assert(n == 7);
    
        return 0;
    }
    

    在這個例子中,n 原本是 4,後來經 += (相加後賦值) 變成 7

    三元運算子

    三元運算子類似於小型的 if 敘述。其虛擬碼如下:

    condition ? a : b
    

    但三元運算子是表達式,和 if 敘述相異。所以,三元運算子會回傳值。

    以下範例用三元運算子求得兩值中較大者:

    #include <assert.h>
    
    int main(void)
    {
        unsigned max = 5 > 3 ? 5 : 3;
        
        assert(max == 5);
    
        return 0;
    }
    

    其他運算子

    以下是一些未歸類的運算子:

    • sizeof:取得資料的寬度
    • *:宣告指標變數、取值
    • &:取址

    以下實例用 sizeof 求得不同資料型態的寬度:

    #include <stdbool.h>
    #include <stdio.h>
    
    int main(void)
    {
        printf("Size of bool: %u\n", sizeof(true));
    
        printf("Size of char: %u\n", sizeof('c'));
    
        printf("Size of short: %u\n", sizeof((short) 3));
        printf("Size of int: %u\n", sizeof(3));
        printf("Size of long: %u\n", sizeof(3l));
        printf("Size of long long: %u\n", sizeof(3ll));
        
        printf("Size of float: %u\n", sizeof(3.4f));
        printf("Size of double: %u\n", sizeof(3.4));
        printf("Size of double: %u\n", sizeof(3.4l));
    
        return 0;
    }
    

    sizeof 長得很像函式,但是 sizeof 是運算子,需注意。

    運算子優先順序

    如果同一行敘述內用到多個運算子時,要如何確認那個運算子會先運算呢?正統的方法是透過查詢運算子優先順序來確認。

    但更好的方法是藉由簡化敘述,在同一行敘述內不要寫過於複雜的運算。必要時也可以用中括號提升運算子的優先順序。即使自己很熟運算子的優先順序,也不保證團隊成員都很熟。我們應該要撰寫簡單明暸的敘述,減少大家對程式碼的猜測。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    標籤: C 語言, OPERATOR