黑马程序员-3-C语言学习(内存剖析,进制)
黑马程序员-3-C语言学习(内存剖析,进制)
明天过后1 发表于4年前
黑马程序员-3-C语言学习(内存剖析,进制)
  • 发表于 4年前
  • 阅读 47
  • 收藏 0
  • 点赞 0
  • 评论 0

移动开发云端新模式探索实践 >>>   

摘要: 进制,变量内存存储分析, 类型说明符, 位运算

---------------------- Java培训.Net培训Android培训IOS培训、期待与您交流! ----------------------

1. 进制

    进制是一种表示数值的的形式,电脑存储数据都是以 0 和 1 来存储数据的,因此我们必须了解进制概念。

    a. 进制常用类别

        (1) 十进制 : 

            1) 用0 - 9 来表示数值,如 5 ,17 ,107 等等

            2) 特点 : 数值由0 - 9 之间数字组成,逢九进一

            3) 书写格式 : 直接写出十进制数值

        (2) 二进制 : 

            1) 用 0 和 1 来表示数值,如 0b0101 ,0b111 等

            2) 特点 : 只有0和1,逢2进1

            3)  书写格式:以0b开头

        (3) 八进制 : 

            1) 用 0 - 7 来表示数值,如 07 ,010 等

            2) 特点 : 数值由0 - 7 之间数字组成,逢八进一

            3)  书写格式:以0开头

        (4) 十六进制 : 

            1) 用 0 - F 来表示数值,如 0x9 ,0xA ,0xFF 等

            2) 特点 : 数值由0 - F 之间数字组成,逢十六进一

            3) 书写格式:以0x开头            

    b. printf 函数输出格式控制

格式符 功能
%d
以带符号的十进制形式输出整数 ( 正数不输出正号+ )
%o 以不带符号的八进制形式输出整数
%x 以不带符号的十六进制形式输出整数
%u 以不带符号的十进制形式输出整数
%c 输出一个字符
%s 输出一个或多个字符
%f 以小数形式输出单、双精度,默认输出6位小数
%e 以标准指数形式输出单、双精度数,数字部分小数位数为6位


2. 变量内存存储分析

    a. 内存存储细节特点

        (1) 内存都是以"字节"为存储单位

        (2) 每个"字节"都有相应的存储地址(如下图)

        (3) 变量的存储地址为该变量的存储空间的首地址

        (4) 变量的类型是指针访问内存的寻址方式(比如 : int 类型告诉指针从改变量地址开始一次寻址4个字节)

        (5) 复杂变量内存分配符合内存对齐算法

            

    b. 变量存储

        (1) 变量类型分配空间的大小跟编译器有关

            


16位编译器 32位编译器 64位编译器
char 1
1 1
int 2 4 4
float 4 4 4
double 8 8 8

        (2) 变量内存分配存储特点   

            1) 内存寻址方式由大到小

            2) 内存中存储的都是二进制形式

            3) 变量的地址是该变量的首地址

    #include <stdio.h>    
    /*
        变量内存分配原则:
        从高地址向低地址分配空间给变量
    */
    int main(){
        int a;
        int b;
        printf("a address = %p\n",&a);  // 打印结果 :  a address = 0028FF44
        printf("b address = %p\n",&b);  // 打印结果 :  b address = 0028FF40
        
        system("pause");
        return 0;    
    }

    c. 负数在内存中存储

        (1) 了解负数存储形式前,先学习原码,反码,补码的概念

              1) 原码 : 最高位为符号位, 0代表正数,1代表负数,其余位数用来表示数值大小。

                    如 1 的原码 : 0000 0000 0000 0000 0000 0000 0000 0001 

                        -1的原码 : 1111 1111 1111 1111 1111 1111 1111 1111

             2) 反码 : 正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外.

                    如 1 的反码 : 0000 0000 0000 0000 0000 0000 0000 0001

                        -1的反码 : 1000 0000 0000 0000 0000 0000 0000 0000

             3) 补码 : 原码符号位不变,先将原码减去1,最后数值各位取反

                    如 1 的补码 : 0111 1111 1111 1111 1111 1111 1111 1111

                       -1 的补码 : 1000 0000 0000 0000 0000 0000 0000 0001

        (2) 十进制正整数求负整数方式:

            首先了解二进制加减法运算法则 :       

            0-0=1-1=0 

            1-0=1 

            0-1=1(向高位借位) 

            十进制运算 : 0 - 1 = -1;

            把这条等式转化为二进制来运算 : 

                0000 0000 0000 0000 0000 0000 0000 0000   // 0

             -  0000 0000 0000 0000 0000 0000 0000 0001  // 1

        -----------------------------------------------------------------

              1 1111 1111 1111 1111 1111 1111 1111 1111

            由于整数在内存中只存储32为因此上述结果会舍弃第33位 , 那么 -1 二进制 表示方式 : 1111 1111 1111 1111 1111 1111 1111 1111

        (3) 为了方便我们可以把正负整数转化转化为以下规律 : 

            求一个二进制的相反数 : 符号位+1,数值位为取该数的补码数值位


3. 类型说明符

    a. short 和 long

        short == short int

            long == long int

            long long == long long int

        不同编译器对这些类型分配空间规则不一样 :

            

        由于编译器众多,因此需要一个约定来规范 :         

                short跟int至少为16位(2字节)

                long至少为32位(4字节)

                short的长度不能大于int,int的长度不能大于long

                char一定为为8位(1字节),毕竟char是我们编程能用的最小数据类型


            由于这些数据类型都是有符号的,因此它们的取值范围可以根据它们内存中存储的二进制形式进行推敲 :

                如 : short 占用 2 个byte ,16位 最高位为符号位,取值范围可以这样表示 : -2^15 ~ (2^15 ) - 1  

                其它类型以此类推

    b. signed 和 unsigned

            signed 和 unsigned 主要用来描述该类型的最高位是否用来充当符号位,当然如果不用来充当符号位那么该类型就没有负数部分;

                signed : 表示有符号,就是说最高位充当符号位  // signed int == int 取值范围 : -2^31 ~ (2^31 ) - 1  

                unsigned : 表示没有符号,也就是说最高位不用来充当符号位 // unsigned int 取值范围 : 0~2^32 -1


4. 位运算

    学习位运算前首先得了解数值而二进制表示方式。

    a. & 按位与

        运算法则 : 两个数的二进制位对齐,逐位比较,两个二进位均为1时,结果位才为1,否则为0。

            如 9 & 5 = 1

                0000 0000 0000 0000 0000 0000 0000 1001

          &   0000 0000 0000 0000 0000 0000 0000 0101

         -----------------------------------------------------------------

           0000 0000 0000 0000 0000 0000 0000 0001

        规律 : 二进制中,与1相&就保持原位,与0相&就为0

    b. | 按位或

        运算法则 : 两个数的二进制位对齐,逐位比较,二进位有一个为1时,结果位就为1,否则为0。

            如 9 | 5 = 13

                    0000 0000 0000 0000 0000 0000 0000 1001

              |     0000 0000 0000 0000 0000 0000 0000 0101

         -----------------------------------------------------------------

             0000 0000 0000 0000 0000 0000 0000 1101

    c. ^ 按位异或

        运算法则 : 两个数的二进制位对齐,逐位比较,(不相同)时,结果为1,否则为0 

            如 9 ^ 5 = 12

                    0000 0000 0000 0000 0000 0000 0000 1001

              ^    0000 0000 0000 0000 0000 0000 0000 0101

         -----------------------------------------------------------------

             0000 0000 0000 0000 0000 0000 0000 1100

            规律 :          

                    相同整数相^的结果是0。比如5^5=0

                    多个整数相^的结果跟顺序无关。比如5^6^7=5^7^6

                    因此得出结论:a^b^a = b

        

        /*
            利用^交换两个变量的值
        */
        int main(){
            int a =10;
            int b =100;
            a = a ^ b; // a--> a ^ b
            b = a ^ b; // b--> a ^ b ^b == 10
            b = a ^ b; // b--> a ^ b ^ a ^ b ^b ==100
        }

    d. ~ 取反

        运算法则 : 对整数的各二进位进行取反,符号位也取反(0变1,1变0)

                如 ~9 = -10

                ~  0000 0000 0000 0000 0000 0000 0000 1001

         -----------------------------------------------------------------

                    1111 1111 1111 1111 1111 1111 1111 0110

    e. << 左移

        运算法则 : 把整数a的各二进位全部左移n位,高位丢弃,低位补0。左移n位其实就是乘以2的n次方。

          注意 : 由于左移是丢弃最高位,0补最低位,所以符号位也会被丢弃,左移出来的结果值可能会改变正负性

    f. >> 右移

      运算法则 : 

            把整数a的各二进位全部右移n位,保持符号位不变。右移n位其实就是除以2的n次方

            为正数时, 符号位为0,最高位补0

            为负数时,符号位为1,最高位是补0或是补1 取决于编译系统的规定


5. 位运算符的应用( 二进制字符串 和 整数之间的转换 )

    a. 打印出整数的二进制字符串

      /*
        把一个整数打印出它的二进制字符串
        num : 所要打印的整数
     */
    void printBinary(int num){
        // 总共要打印多少位数字
        int size = sizeof(num) * 8;
        printf("size = %d\n",size);
        while ( size > 0 ) {
            // 每打印4位数字输出一个空格
            if ( size % 4 ==0 ) {
                printf(" ");
            }
            size --;
            printf("%d",(num >> size) & 1);
             
        }
        printf("\n");
         
    }

    b. 二进制字符串转化为整数

/*
    计算一个二进制字符串的整数值
    例如 : 0000 0000 0000 0000 0000 0000 0000 0110  --> 6
    参数 : const char *s 所要计算的二进制字符串,字符必须为32位自能包含空格(可选),或则0 和1
    返回计算结果
  
 */
int strToInt(const char *s){
    // 最总计算的值
    int num = 0;
    int i = 0;
    unsigned long size = strlen(s);
   // printf("size = %ld\n",size);
    char c = 'a';
    // 遍历该字符串
    while ( size > 0 ) {
        // 从最后一个字符取出字符来进行检查
        c = s[size-1];
        if ( c == '0' || c == '1' ){ // 如果是0 和 1
            num += ( c == '1' ? 1 << i : 0 );
            i++;
            size--;
        } else if ( c == ' ' ){
            size--;
            continue;
        } else {
            return -1;
        }
         
    }
    return num;
}




---------------------- Java培训.Net培训Android培训IOS培训、期待与您交流! ---------------------- 

 详情请查看:http://edu.csdn.net/heima

 

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 2
博文 20
码字总数 36166
×
明天过后1
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: