10-C语言进制和位运算

作者: 极客江南 | 来源:发表于2018-08-30 16:53 被阅读321次

    进制基本概念

    • 什么是进制?

      • 进制是一种计数的方式,数值的表示形式
    • 常见的进制

      • 十进制、二进制、八进制、十六进制
    • 进制书写的格式和规律

      • 十进制 0、1、2、3、4、5、6、7、8、9 逢十进一
      • 二进制 0、1 逢二进一
        • 书写形式:需要以0b或者0B开头,例如: 0b101
      • 八进制 0、1、2、3、4、5、6、7 逢八进一
        • 书写形式:在前面加个0,例如: 061
      • 十六进制 0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F 逢十六进一
      • 书写形式:在前面加个0x或者0X,例如: 0x45
    • 练习

      • 1.用不同进制表示如下有多少个方格
      • 2.判断下列数字是否合理
      00011  0x001  0x7h4  10.98  0986  .089-109
      +178  0b325  0b0010  0xffdc 96f 96.0f 96.oF  -.003
      

    进制转换

    • 10 进制转 2 进制
      • 除2取余, 余数倒序; 得到的序列就是二进制表示形式
      • 例如: 将十进制(97) 10转换为二进制数

    • 2 进制转 10 进制
      • 每一位二进制进制位的值 * 2的当前索引次幂; 再将所有位求出的值相加
      • 例如: 将二进制01100100转换为十进制
      01100100
      索引从右至左, 从零开始
      第0位: 0 * 2^0 = 0;
      第1位: 0 * 2^1 = 0;
      第2位: 1 * 2^2 = 4;
      第3位: 0 * 2^3 = 0;
      第4位: 0 * 2^4 = 0;
      第5位: 1 * 2^5 = 32;
      第6位: 1 * 2^6 = 64;
      第7位: 0 * 2^7 = 0;
      最终结果为: 0 + 0 + 4 + 0 + 0 + 32 + 64 + 0 = 100
      

    • 2 进制转 8 进制
      • 三个二进制位代表一个八进制位, 因为3个二进制位的最大值是7,而八进制是逢8进1
      • 例如: 将二进制01100100转换为八进制数
      从右至左每3位划分为8进制的1位, 不够前面补0
      001 100 100
      第0位: 100 等于十进制 4
      第1位: 100 等于十进制 4
      第2位: 001 等于十进制 1
      最终结果: 144就是转换为8进制的值
      

    • 2 进制转 16 进制
      • 四个二进制位代表一个十六进制位,因为4个二进制位的最大值是15,而十六进制是逢16进1
      • 例如: 将二进制01100100转换为十六进制数
      从右至左每4位划分为16进制的1位, 不够前面补0
      0110 0100
      第0位: 0100 等于十进制 4
      第1位: 0110 等于十进制 6
      最终结果: 64就是转换为16进制的值
      

    • 其它进制转换为十进制
      • 系数 * 基数 ^ 索引 之和
          十进制           -->          十进制
         12345   =  10000 + 2000 + 300 + 40 + 5
                 =  (1 * 10 ^ 4)  + (2 * 10 ^ 3) + (3 * 10 ^ 2) + (4 * 10 ^ 1) + (5 * 10 ^ 0)
                 =  (1 * 10000) + (2 + 1000) + (3 * 100) + (4 * 10) + (5 * 1)
                 =  10000 + 2000 + 300 + 40 + 5
                 =  12345
         
         规律:
         其它进制转换为十进制的结果 = 系数 * 基数 ^ 索引 之和
         
         系数: 每一位的值就是一个系数 
         基数: 从x进制转换到十进制, 那么x就是基数
         索引: 从最低位以0开始, 递增的数
      
         二进制        -->      十进制
         543210
         101101 = (1 * 2 ^ 5) + (0 * 2 ^ 4) + (1 * 2 ^ 3) + (1 * 2 ^ 2) + (0 * 2 ^ 1) + (1 * 2 ^ 0)
                = 32 + 0 + 8 + 4 + 0 + 1
                = 45
         
         八进制        -->     十进制
         016  =   (0 * 8 ^ 2) + (1 * 8 ^ 1) + (6 * 8 ^ 0)
              =    0  + 8 + 6
              =    14
         
         十六进制      -->      十进制
         0x11f =  (1 * 16 ^ 2) + (1 * 16 ^ 1) + (15 * 16 ^ 0)
               =   256  + 16 + 15
               =   287
      

    • 十进制快速转换为其它进制
      • 十进制除以基数取余, 倒叙读取
         十进制        -->     二进制
         100          -->    1100100
         100 / 2   = 50     0
         50  / 2   = 25     0
         25  / 2   = 12     1
         12  / 2   = 6      0
         6   / 2   = 3      0
         3   / 2   = 1      1
         1   / 2   = 0      1
         
         
         十进制        -->     八进制
         100          -->     144
         100 / 8    = 12    4
         12  / 8    = 1     4
         1   / 8    = 0     1
         
         十进制        -->     十六进制
         100          --> 64
         100 / 16   =  6    4
         6   / 16   =  0    6
      

    十进制小数转换为二进制小数

    • 整数部分,直接转换为二进制即可
    • 小数部分,使用"乘2取整,顺序排列"
      • 用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,直到积中的小数部分为零,或者达到所要求的精度为止
      • 然后把取出的整数部分按顺序排列起来, 即是小数部分二进制
    • 最后将整数部分的二进制和小数部分的二进制合并起来, 即是一个二进制小数
    • 例如: 将12.125转换为二进制
    // 整数部分(除2取余)
      12
    /  2
    ------
       6    // 余0
    /  2
    ------
       3    // 余0
    /  2
    ------
       1   // 余1
    /  2
    ------
      0   // 余1
    //12 --> 1100
      
    // 小数部分(乘2取整数积)
      0.125
    *     2
      ------
       0.25  //0
       0.25
    *     2
      ------
        0.5  //0
        0.5
    *     2
      ------
        1.0  //1
        0.0
    // 0.125 --> 0.001
    
    // 12.8125 --> 1100.001
    

    二进制小数转换为十进制小数

    • 整数部分按照二进制转十进制即可
    • 小数部分从最高位开始乘以2的负n次方, n从1开始
    • 例如: 将 1100.001转换为十进制
    // 整数部分(乘以2的n次方, n从0开始)
    0 * 2^0 = 0
    0 * 2^1 = 0
    1 * 2^2 = 4
    1 * 2^3 = 8
     // 1100 == 8 + 4 + 0 + 0 == 12
    
    // 小数部分(乘以2的负n次方, n从0开始)
    0 * (1/2) = 0
    0 * (1/4) = 0
    1 * (1/8) = 0.125
    // .100 == 0 + 0 + 0.125 == 0.125
    
    // 1100.001  --> 12.125
    
    • 练习:
      • 将0.8125转换为二进制
      • 将0.1101转换为十进制
      0.8125
    *      2
    --------
       1.625  // 1
       0.625
    *      2
    --------
        1.25 // 1
        0.25
    *      2
    --------
         0.5 // 0
    *      2
    --------
        1.0 // 1
        0.0
    
    // 0. 8125  --> 0.1101
    
    1*(1/2) = 0.5
    1*(1/4)=0.25
    0*(1/8)=0
    1*(1/16)=0.0625
    
    //0.1101 --> 0.5 + 0.25 + 0 + 0.0625 == 0.8125
    

    原码反码补码

    • 计算机只能识别0和1, 所以计算机中存储的数据都是以0和1的形式存储的
    • 数据在计算机内部是以补码的形式储存的, 所有数据的运算都是以补码进行的
    • 正数的原码、反码和补码
      • 正数的原码、反码和补码都是它的二进制
      • 例如: 12的原码、反码和补码分别为
        • 0000 0000 0000 0000 0000 0000 0000 1100
        • 0000 0000 0000 0000 0000 0000 0000 1100
        • 0000 0000 0000 0000 0000 0000 0000 1100
    • 负数的原码、反码和补码
      • 二进制的最高位我们称之为符号位, 最高位是0代表是一个正数, 最高位是1代表是一个负数
      • 一个负数的原码, 是将该负数的二进制最高位变为1
      • 一个负数的反码, 是将该数的原码除了符号位以外的其它位取反
      • 一个负数的补码, 就是它的反码 + 1
      • 例如: -12的原码、反码和补码分别为
        0000 0000 0000 0000 0000 0000 0000 1100 // 12二进制
        1000 0000 0000 0000 0000 0000 0000 1100 // -12原码
        1111 1111 1111 1111 1111 1111 1111 0011  // -12反码
        1111 1111 1111 1111 1111 1111 1111 0100 // -12补码
      
    • 负数的原码、反码和补码逆向转换
      • 反码 = 补码-1
      • 原码= 反码最高位不变, 其它位取反
        1111 1111 1111 1111 1111 1111 1111 0100 // -12补码
        1111 1111 1111 1111 1111 1111 1111 0011  // -12反码
        1000 0000 0000 0000 0000 0000 0000 1100 // -12原码
      

    • 为什么要引入反码和补码
      • 在学习本节内容之前,大家必须明白一个东西, 就是计算机只能做加法运算, 不能做减法和乘除法, 所以的减法和乘除法内部都是用加法来实现的
        • 例如: 1 - 1, 内部其实就是 1 + (-1);
        • 例如: 3 * 3, 内部其实就是 3 + 3 + 3;
        • 例如: 9 / 3, 内部其实就是 9 + (-3) + (-3) + (-3);
      • 首先我们先来观察一下,如果只有原码会存储什么问题
        • 很明显, 通过我们的观察, 如果只有原码, 1-1的结果不对
          // 1 + 1
           0000 0000 0000 0000 0000 0000 0000 0001 // 1原码
          +0000 0000 0000 0000 0000 0000 0000 0001 // 1原码
           ---------------------------------------
           0000 0000 0000 0000 0000 0000 0000 0010  == 2
        
           // 1 - 1; 1 + (-1);
           0000 0000 0000 0000 0000 0000 0000 0001 // 1原码
          +1000 0000 0000 0000 0000 0000 0000 0001 // -1原码
           ---------------------------------------
           1000 0000 0000 0000 0000 0000 0000 0010 == -2
        
      • 正是因为对于减法来说,如果使用原码结果是不正确的, 所以才引入了反码
        • 通过反码计算减法的结果, 得到的也是一个反码;
        • 将计算的结果符号位不变其余位取反,就得到了计算结果的原码
        • 通过对原码的转换, 很明显我们计算的结果是-0, 符合我们的预期
        // 1 - 1; 1 + (-1);
        0000 0000 0000 0000 0000 0000 0000 0001 // 1反码
        1111 1111 1111 1111 1111 1111 1111 1110   // -1反码
        ---------------------------------------
        1111 1111 1111 1111 1111 1111 1111 1111 // 计算结果反码
        1000 0000 0000 0000 0000 0000 0000 0000 // 计算结果原码 == -0
      
      • 虽然反码能够满足我们的需求, 但是对于0来说, 前面的负号没有任何意义, 所以才引入了补码
        • 由于int只能存储4个字节, 也就是32位数据, 而计算的结果又33位, 所以最高位溢出了,符号位变成了0, 所以最终得到的结果是0
        // 1 - 1; 1 + (-1);
        0000 0000 0000 0000 0000 0000 0000 0001 // 1补码
        1111 1111 1111 1111 1111 1111 1111 1111   // -1补码
        ---------------------------------------
       10000 0000 0000 0000 0000 0000 0000 0000 // 计算结果补码
        0000 0000 0000 0000 0000 0000 0000 0000 //  == 0
      

    位运算符

    • 程序中的所有数据在计算机内存中都是以二进制的形式储存的。
    • 位运算就是直接对整数在内存中的二进制位进行操作
    • C语言提供了6个位操作运算符, 这些运算符只能用于整型操作数
    符号 名称 运算结果
    & 按位与 同1为1
    | 按位或 有1为1
    ^ 按位异或 不同为1
    ~ 按位取反 0变1,1变0
    << 按位左移 乘以2的n次方
    >> 按位右移 除以2的n次方

    • 按位与:
      • 只有对应的两个二进位均为1时,结果位才为1,否则为0
      • 规律: 二进制中,与1相&就保持原位,与0相&就为0
    9&5 = 1
    
     1001
    &0101
    ------
     0001
    

    • 按位或:
      • 只要对应的二个二进位有一个为1时,结果位就为1,否则为0
    9|5 = 13
    
     1001
    |0101
    ------
     1101
    

    • 按位异或
      • 当对应的二进位相异(不相同)时,结果为1,否则为0
      • 规律:
        • 相同整数相的结果是0。比如55=0
        • 多个整数相^的结果跟顺序无关。例如: 567=576
        • 同一个数异或另外一个数两次, 结果还是那个数。例如: 577 = 5
    9^5 = 12
    
     1001
    ^0101
    ------
     1100
    

    • 按位取反
      • 各二进位进行取反(0变1,1变0)
    ~9 =-10
    0000 0000 0000 0000 0000 1001 // 取反前
    1111 1111 1111 1111 1111 0110 // 取反后
    
    // 根据负数补码得出结果
    1111 1111 1111 1111 1111 0110 // 补码
    1111 1111 1111 1111 1111 0101 // 反码
    1000 0000 0000 0000 0000 1010 // 源码 == -10
    

    • 位运算应用场景:
      • 判断奇偶(按位或)
         偶数: 的二进制是以0结尾
         8   -> 1000
         10  -> 1010
         
         奇数: 的二进制是以1结尾
         9   -> 1001
         11  -> 1011
      
         任何数和1进行&操作,得到这个数的最低位
         1000
        &0001
         -----
         0000  // 结果为0, 代表是偶数
      
         1011
        &0001
         -----
         0001 // 结果为1, 代表是奇数
      
      • 权限系统
        enum Unix {
          S_IRUSR = 256,// 100000000 用户可读
          S_IWUSR = 128,//  10000000 用户可写
          S_IXUSR = 64,//    1000000 用户可执行
          S_IRGRP = 32,//     100000 组可读
          S_IWGRP = 16,//      10000 组可写
          S_IXGRP = 8,//        1000 组可执行
          S_IROTH = 4,//         100 其它可读
          S_IWOTH = 2,//          10 其它可写
          S_IXOTH = 1 //           1 其它可执行
         };
      // 假设设置用户权限为可读可写
      printf("%d\n", S_IRUSR | S_IWUSR); // 384 // 110000000
      
      • 交换两个数的值(按位异或)
       a = a^b;
       b = b^a;
       a = a^b;
      

    • 按位左移
      • 把整数a的各二进位全部左移n位,高位丢弃,低位补0
        • 由于左移是丢弃最高位,0补最低位,所以符号位也会被丢弃,左移出来的结果值可能会改变正负性
      • 规律: 左移n位其实就是乘以2的n次方
    2<<1; //相当于 2 *= 2 // 4
      0010
    <<0100
    
    2<<2; //相当于 2 *= 2^2; // 8
      0010
    <<1000
    
    • 按位右移
      • 把整数a的各二进位全部右移n位,保持符号位不变
        • 为正数时, 符号位为0,最高位补0
        • 为负数时,符号位为1,最高位是补0或是补1(取决于编译系统的规定)
      • 规律: 快速计算一个数除以2的n次方
    2>>1; //相当于 2 /= 2 // 1
      0010
    >>0001
    4>>2; //相当于 4 /= 2^2 // 1
      0100
    >>0001
    
    • 练习:
      • 写一个函数把一个10进制数按照二进制格式输出
    #include <stdio.h>
    void printBinary(int num);
    int main(int argc, const char * argv[]) {
        printBinary(13);
    }
    void printBinary(int num){
        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);
        }
    }
    

    变量内存分析

    • 内存模型
      • 内存模型是线性的(有序的)
      • 对于 32 机而言,最大的内存地址是2^32次方bit(4294967296)(4GB)
      • 对于 64 机而言,最大的内存地址是2^64次方bit(18446744073709552000)(171亿GB)
    • CPU 读写内存
      • CPU 在运作时要明确三件事
        • 存储单元的地址(地址信息)
        • 器件的选择,读 or 写 (控制信息)
        • 读写的数据 (数据信息)
    • 如何明确这三件事情
      • 通过地址总线找到存储单元的地址
      • 通过控制总线发送内存读写指令
      • 通过数据总线传输需要读写的数据
    • 地址总线: 地址总线宽度决定了CPU可以访问的物理地址空间(寻址能力)
      • 例如: 地址总线的宽度是1位, 那么表示可以访问 0 和 1的内存
      • 例如: 地址总线的位数是2位, 那么表示可以访问 00、01、10、11的内存
    • 数据总线: 数据总线的位数决定CPU单次通信能交换的信息数量
      • 例如: 数据总线:的宽度是1位, 那么一次可以传输1位二进制数据
      • 例如: 地址总线的位数是2位,那么一次可以传输2位二进制数据
    • 控制总线: 用来传送各种控制信号
    • 写入流程
      • CPU 通过地址线将找到地址为 FFFFFFFB 的内存
      • CPU 通过控制线发出内存写入命令,选中存储器芯片,并通知它,要其写入数据。
      • CPU 通过数据线将数据 8 送入内存 FFFFFFFB 单元中


    • 读取流程
      • CPU 通过地址线将找到地址为 FFFFFFFB 的内存
      • CPU 通过控制线发出内存读取命令,选中存储器芯片,并通知它,将要从中读取数据
      • 存储器将 FFFFFFFB 号单元中的数据 8 通过数据线送入 CPU寄存器中


    • 变量的存储原则
      • 先分配字节地址大内存,然后分配字节地址小的内存(内存寻址是由大到小)
      • 变量的首地址,是变量所占存储空间字节地址(最小的那个地址 )
      • 低位保存在低地址字节上,高位保存在高地址字节上
      10的二进制: 0b00000000 00000000 00000000 00001010
                 高字节←                        →低字节
      

    Qt Creator编译过程做了什么?

    • 当我们按下运行按钮的时, 其实Qt Creator编译器做了5件事情
      • 对源文件进行预处理, 生成预处理文件
      • 对预处理文件进行编译, 生成汇编文件
      • 对汇编文件进行编译, 生成二进制文件
      • 对二进制文件进行链接, 生成可执行文件
      • 运行可执行文件

    • Qt Creator编译过程验证
      • 1.编写代码, 保存源文件:
      #include <stdio.h>
      int main(){
          printf("hello lnj\n");
          return 0;
      }
      
      • 2.执行预处理编译
      • 执行预处理编译后生成的文件
      • 打开预处理编译后生成的文件
        • 处理源文件中预处理相关的指令
        • 处理源文件中多余注释等

      • 3.执行汇编编译
      • 执行汇编编译后生成的文件
      • 打开汇编编译后生成的文件

      • 4.执行二进制编译
      • 执行二进制编译后生成的文件
      • 打开二进制编译后生成的文件

      • 5.执行链接操作
        • 将依赖的一些C语言函数库和我们编译好的二进制合并为一个文件
      • 执行链接操作后生成的文件

      • 6.运行链接后生成的文件

    计算机是运算过程分析

    • 1.编写一个简单的加法运算
    • 2.调试编写好的代码, 查看对应的汇编文件
    • 结论:
      • 1.通过地址线找到对应地址的存储单元
      • 2.通过控制线发送内存读取指令
      • 3.通过数据线将内存中的值传输到CPU寄存器中
      • 4.在CPU中完成计算操作
      • 5.通过地址线找到对应地址的存储单元
      • 6.通过控制线发送内存写入指令
      • 7.通过数据线将计算结果传输到内存中

    char类型内存存储细节

    • char类型基本概念
      • char是C语言中比较灵活的一种数据类型,称为“字符型”
      • char类型变量占1个字节存储空间,共8位
      • 除单个字符以外, C语言的的转义字符也可以利用char类型存储
    字符 意义
    \b 退格(BS)当前位置向后回退一个字符
    \r 回车(CR),将当前位置移至本行开头
    \n 换行(LF),将当前位置移至下一行开头
    \t 水平制表(HT),跳到下一个 TAB 位置
    \0 用于表示字符串的结束标记
    \ 代表一个反斜线字符 \
    \" 代表一个双引号字符"
    \' 代表一个单引号字符'
    • char型数据存储原理
      • 计算机只能识别0和1, 所以char类型存储数据并不是存储一个字符, 而是将字符转换为0和1之后再存储
      • 正是因为存储字符类型时需要将字符转换为0和1, 所以为了统一, 老美就定义了一个叫做ASCII表的东东
      • ASCII表中定义了每一个字符对应的整数


        char ch1 = 'a'; 
        printf("%i\n", ch1); // 97
    
        char ch2 = 97;
        printf("%c\n", ch2); // a
    
    • char类型注意点
      • char类型占一个字节, 一个中文字符占3字节(unicode表),所有char不可以存储中文
      char c = '我'; // 错误写法
      
      • 除转义字符以外, 不支持多个字符
      char ch = 'ab'; // 错误写法
      
      • char类型存储字符时会先查找对应的ASCII码值, 存储的是ASCII值, 所以字符6和数字6存储的内容不同
      char ch1 = '6'; // 存储的是ASCII码 64
      char ch2 = 6; //  存储的是数字 6
      
    • 练习
      • 定义一个函数, 实现输入一个小写字母,要求转换成大写输出

    类型说明符

    • 类型说明符基本概念
      • C语言提供了说明长度说明符号位的两种类型说明符, 这两种类型说明符一共有4个:
        • short 短整型 (说明长度)
        • long 长整型 (说明长度)
        • signed 有符号型 (说明符号位)
        • unsigned 无符号型 (说明符号位)
    • 这些说明符是用来修饰int类型的,所以在使用时可以省略int
    • 这些说明符都属于C语言关键字

    short和long

    • short和long可以提供不同长度的整型数,也就是可以改变整型数的取值范围。
      • 在64bit编译器环境下,int占用4个字节(32bit),取值范围是-2^31 ~ 2^31-1;
      • short占用2个字节(16bit),取值范围是-2^15 ~ 2^15-1;
      • long占用8个字节(64bit),取值范围是-2^63 ~ 2^63-1
    • 总结一下:在64位编译器环境下:
      • short占2个字节(16位)
      • int占4个字节(32位)
      • long占8个字节(64位)。
      • 因此,如果使用的整数不是很大的话,可以使用short代替int,这样的话,更节省内存开销。
    • 世界上的编译器林林总总,不同编译器环境下,int、short、long的取值范围和占用的长度又是不一样的。比如在16bit编译器环境下,long只占用4个字节。不过幸运的是,ANSI \ ISO制定了以下规则:
      • short跟int至少为16位(2字节)
      • long至少为32位(4字节)
      • short的长度不能大于int,int的长度不能大于long
      • char一定为为8位(1字节),毕竟char是我们编程能用的最小数据类型
    • 可以连续使用2个long,也就是long long。一般来说,long long的范围是不小于long的,比如在32bit编译器环境下,long long占用8个字节,long占用4个字节。不过在64bit编译器环境下,long long跟long是一样的,都占用8个字节。
    #include <stdio.h>
    
    int main()
    {
        // char占1个字节, char的取值范围 -2^7~2^7
        char num = 129;
        printf("size = %i\n", sizeof(num)); // 1
        printf("num = %i\n", num); // -127
        // short int 占2个字节, short int的取值范围 -2^15~2^15-1
        short int num1 = 32769;// -32767
        printf("size = %i\n", sizeof(num1)); // 2
        printf("num1 = %hi\n", num1);
    
        // int占4个字节, int的取值范围 -2^31~2^31-1
        int num2 = 12345678901;
        printf("size = %i\n", sizeof(num2)); // 4
        printf("num2 = %i\n", num2);
    
        // long在32位占4个字节, 在64位占8个字节
        long int num3 = 12345678901;
        printf("size = %i\n", sizeof(num3)); // 4或8
        printf("num3 = %ld\n", num3);
    
        // long在32位占8个字节, 在64位占8个字节 -2^63~2^63-1
        long long int num4 = 12345678901;
        printf("size = %i\n", sizeof(num4)); // 8
        printf("num4 = %lld\n", num4);
        
        // 由于short/long/long long一般都是用于修饰int, 所以int可以省略
        short num5 = 123;
        printf("num5 = %lld\n", num5);
        long num6 = 123;
        printf("num6 = %lld\n", num6);
        long long num7 = 123;
        printf("num7 = %lld\n", num7);
        return 0;
    }
    

    signed和unsigned

    • 首先要明确的:signed int等价于signed,unsigned int等价于unsigned
    • signed和unsigned的区别就是它们的最高位是否要当做符号位,并不会像short和long那样改变数据的长度,即所占的字节数。
      • signed:表示有符号,也就是说最高位要当做符号位。但是int的最高位本来就是符号位,因此signed和int是一样的,signed等价于signed int,也等价于int。signed的取值范围是-2^31 ~ 2^31 - 1
      • unsigned:表示无符号,也就是说最高位并不当做符号位,所以不包括负数。
      • 因此unsigned的取值范围是:0000 0000 0000 0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111 1111 1111 1111,也就是0 ~ 2^32 - 1
    #include <stdio.h>
    
    int main()
    {
        // 1.默认情况下所有类型都是由符号的
        int num1 = 9;
        int num2 = -9;
        int num3 = 0;
        printf("num1 = %i\n", num1);
        printf("num2 = %i\n", num2);
        printf("num3 = %i\n", num3);
    
        // 2.signed用于明确说明, 当前保存的数据可以是有符号的, 一般情况下很少使用
        signed int num4 = 9;
        signed int num5 = -9;
        signed int num6 = 0;
        printf("num4 = %i\n", num4);
        printf("num5 = %i\n", num5);
        printf("num6 = %i\n", num6);
    
        // signed也可以省略数据类型, 但是不推荐这样编写
        signed num7 = 9;
        printf("num7 = %i\n", num7);
       
    
        // 3.unsigned用于明确说明, 当前不能保存有符号的值, 只能保存0和正数
        // 应用场景: 保存银行存款,学生分数等不能是负数的情况
        unsigned int num8 = -9;
        unsigned int num9 = 0;
        unsigned int num10 = 9;
        // 注意: 不看怎么存只看怎么取
        printf("num8 = %u\n", num8);
        printf("num9 = %u\n", num9);
        printf("num10 = %u\n", num10);
        return 0;
    }
    
    • 注意点:
      • 修饰符号的说明符可以和修饰长度的说明符混合使用
      • 相同类型的说明符不能混合使用
        signed short int num1 = 666;
        signed unsigned int num2 = 666; // 报错
    

    相关文章

      网友评论

        本文标题:10-C语言进制和位运算

        本文链接:https://www.haomeiwen.com/subject/fnmejftx.html