【C語言】6.進制,位運算,補碼反碼,數組等

  • 進制

    • 二進制以0b0B開頭,如0b001。(數字0不是字母O)

    • 八進制以0開頭,如067。

    • 十六進制以0x0X開頭,如0x48B。

    • 打印一個數的八進制和十六進制:

      int a = 13;
      printf("10->8: %o\n", a);
      printf("10->16: %x\n", a);
      
    • 定義一個二進制數、八進制數、十六進制,打印其對應的10 進制

      int a = 0b00000000000000000000000000001101;
      printf("2->10: %d\n", a);
      
      a = 015;
      printf("8->10: %d\n", a);
      
      a = 0xd;
      printf("16->10: %d\n", a);
      
      輸出結果:
      2->10: 13
      8->10: 13
      16->10: 13
      
    • 進制轉換

  • 原碼,反碼,補碼

    • 正數的原碼,反碼,補碼都一樣。三碼合一

    • 負數的第一位為符號位,反碼是原碼取反,補碼是反碼加一。

    • 正數和負數在計算機的內存中都以補碼的形式存在。

    • 對于負數, 補碼表示方式也是人腦無法直觀看出其數值的。 通常也需要轉換成原碼在計算其數值。

    • 為什么要引入反碼和補碼?

      • 現在我們知道了計算機可以有三種編碼方式表示一個數. 對于正數因為三種編碼方式的結果都相同, 所以不需要過多解釋。

        但是對于負數, 可見原碼, 反碼和補碼是完全不同的. 既然原碼才是被人腦直接識別并用于計算表示方式, 為何 還會有反碼和補碼呢?

        首先, 因為人腦可以知道第一位是符號位, 在計算的時候我們會根據符號位, 選擇對真值區域的加減。但是對于計算機, 加減乘數已經是最基礎的運算, 要設計的 盡量簡單. 計算機辨別"符號位"顯然會讓計算機的基礎電路設計變得十分復雜! 于是人們想出了 將符號位也參與運算的方法. 我們知道, 根據運算法則減去一個正數等于加上一個負數, 即: 1-1 = 1 + (-1) = 0 , 所以機器可以只有加法而沒有減法, 這樣計算機運算的設計就更簡單了.

      • 例:

        計算十進制的表達式: 1-1=0

        1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

        如果用原碼表示, 讓符號位也參與計算, 顯然對于減法來說, 結果是不正確的.這也就是為何計算 機內部不使用原碼表示一個數.

        為了解決原碼做減法的問題, 出現了反碼:

           1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原           
         = [0000 0001]反 + [1111 1110]反
         = [1111 1111]反
         = [1000 0000]原 (1111 1111,符號位不變,其他為逐位取反)
         = -0
        

        發現用反碼計算減法, 結果的真值部分是正確的. 而唯一的問題其實就出現在"0"這個特殊的數值 上. 雖然人們理解上+0和-0是一樣的, 但是0帶符號是沒有任何意義的. 而且會有[0000 0000]原和 [1000 0000]原兩個編碼表示0。

        于是補碼的出現, 解決了0的符號以及兩個編碼的問題:

           1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原
         = [0000 0001]補 + [1111 1111]補
         = [0000 0000]補
         = [0000 0000]原
        
        • 這樣0用[0000 0000]表示, 而以前出現問題的-0則不存在了.而且可以用[1000 0000]表示-128: (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]補 + [1000 0001]補 = [1000 0000]補
        • -1-127的結果應該是-128, 在用補碼運算的結果中, [1000 0000]補 就是-128. 但是注意因為實際 上是使用以前的-0的補碼來表示-128, 所以-128并沒有原碼和反碼表示.(對-128的補碼表示[1000 0000]補算出來的原碼是[0000 0000]原, 這是不正確的)
  • 位運算符

    • & 按位與

      • 規律:二進制中,與1相&就保持原位,與0相&就為0。

      • 應用場景:

        1. 按位與運算通常用來對某些位清0或保留某些位。例如把a的高位都清0,保留低八位,那么就a&255。

        2. 判斷奇偶: 將變量a與1做位與運算,若結果是1,則 a是奇數;若結果是0,則 a是偶數。

        3. 任何數和1進行&操作,得到這個數的最低位。

          1001

          &0001

          =0001

          ?

        4. 想把某一位置0。

          11111111

          &11111011

          =11111011

    • | 按位或

    • ^ 按位異或

      • 規律:

        1. 相同整數相的結果是0。比如55=0。
        2. 多個整數相的結果跟順序無關。比如567=576。因此得出結論:__ab^a = b__。
      • 使用位運算實現交換兩個數的值:

        a = a^b;
        b = b^a;
        a = a^b;
        
    • ~ 取反

  • 左移運算符和右移運算符

    • 把整數a的各二進位全部左移n位,高位丟棄,低位補0。左移n位其實就是乘以2的n次方。由于左移是丟棄最高位,0補最低位,所以符號位也會被丟棄,左移出來的結果值可能會改變正負性。

    • 把整數a的各二進位全部右移n位,保持符號位不變。右移n位其實就是除以2的n次方。為正數時, 符號位為0,最高位補0。為負數時,符號位為1,最高位是補0或是補1,取決于編譯系統的規定。

    • 例:

      • 寫一個函數把一個10進制數按照二進制格式輸出

      • 分析:

        13 -----> 0000 0000 0000 0000 0000 0000 0000 1101

        0000 0000 0000 0000 0000 0000 0000 1101 13

        0000 0000 0000 0000 0000 0000 0000 0001 1

        每次取 一個數的最后一個二進制位

        任何一個數和1進行&(按位與)得到任何一個數的二進制的最后

        一位

      • 實現:

            int len = sizeof(int)*8;
            int temp;
            for (int i=0; i<len; i++) {
                temp = num; //每次都在原數的基礎上進行移位運算
                temp = temp>>(31-i); //每次移動的位數
                int t = temp&1; //取出最后一位
                if(i!=0&&i%4==0)printf(" "); printf("%d",t);
            }
        
  • 變量的存儲

    • 一個變量所占用的存儲空間,不僅跟變量類型有關,而且還跟編譯器環境有關系。同一種類型的變量,在不同編譯器環境下所占用的存儲空間又是不一樣的。

    • 任何變量在內存中都是以二進制的形式存儲。一個負數的二進制形式,其實就是對它的正數的二進制形式進行取反后再+1。

    • 變量的首地址,是變量所占存儲空間字節地址最小的那個地址。(因為變量的字節的地址在內存中是由大到小的,以0b00000000 00000000 00000000 00001010為例,00001010屬于低字節,位于這個變量所占4個字節的最上面的那個字節,也就是地址最小的字節。類似Excel中的四個表格,低字節位于最上面(地址最小))。

      ?

  • 類型說明符

    • 在64位編譯器環境下:

      short占2個字節(16位)

      int占4個字節(32位)

      long占8個字節(64位)

      因此,如果使用的整數不是很大的話,可以使用short代替int,這樣的話,更節省內存開銷。

    • ANSI \ ISO制定了以下規則:

      ? short跟int至少為16位(2字節)

      ? long至少為32位(4字節)

      ? short的長度不能大于int,int的長度不能大于long

      ? char一定為為8位(1字節),畢竟char是我們編程能用的最小數據類型

      • 32位編譯器:long long 占 8個字節, long 占 4個字節
      • 64位編譯器:long long 和 long 都是 8個字節
      • long long int == long long
      • long int == long
      • short int == short

      ?

  • char型數據存儲原理

    • char a=‘a' ——> 取出'a'的ASCII碼值,97,然后轉換2進制,存儲在一個字節中。
    • 個人理解,ASCII表就是為字符設計的,因為字符在內存中存儲時首先取出這個字符的ASCII碼值,然后轉換成二進制之后存儲。而且一個字符占一個字節,所以共8位,取值范圍是-27~27-1,所以ASCII表也是0~127。
  • 數組

    • 初始化數組

      • int ages[3] = {4, 6, 9};
      • int nums[10] = {1,2}; // 其余的自動初始化為0
      • int nums[] = {1,2,3,5,6}; // 根據大括號中的元素個數確定數組元素的個數
      • int nums[5] = {[4] = 3,[1] = 2}; // 指定元素個數,同時給指定元素進行初始化
      • int nums[3]; nums[0] = 1; nums[1] = 2; nums[2] = 3; // 先定義,后初始化
      • 定義但是未初始化,數組中有值,但是是垃圾值。
      • 對于數組來說,一旦有元素被初始 化,其他元素都被賦值0。
    • 計算數組中元素的個數

      • int count = sizeof(數組) / sizeof(數組[0]) // 數組的長度 = 數組占用的總字節數 / 數組元素占用的字節數

        ?

    • 數組注意事項

      • 在定義數組的時候[]里面只能寫整型常量或者是返回整型常量的表達式。

        int ages['A'] = {19, 22, 33};

        printf("ages[0] = %d\n", ages[0]);

        int ages[5 + 5] = {19, 22, 33};

        printf("ages[0] = %d\n", ages[0]);

        int ages['A' + 5] = {19, 22, 33};

        printf("ages[0] = %d\n", ages[0])

      • 錯誤寫法

        • 沒有指定元素個數(int nums[] = {1,2,3,5,6}; 這樣是可以的,但是如果先聲明,并沒有初始化,則是錯誤的)

          int a[]; // 錯誤

        • []中不能放變量

          int number = 10;

          int ages[number]; // 不報錯, 但是沒有初始化, 里面是隨機值

    > int number = 10;
    > 
    > int ages[number] = {19, 22, 33} // 直接報錯

  - > int ages10[5];
    > 
    > ages10 = {19, 22, 33};    // 錯誤。只能在定義數組的時候進行一次性(全部賦值)的初始化

  - 訪問數組越界。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容