黑马程序员——C语言基础---内存剖析

来源:互联网 发布:电脑文档整理软件 编辑:程序博客网 时间:2024/05/16 23:56

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

进制

什么是进制

是一种计数的方式,数值的表示形式

 

汉字:十一  十进制:11 二进制:1011 八进制:13 

多种进制:十进制、二进制、八进制、十六进制。也就是说,同一个整数,我们至少有4种表示方式


二进制

特点:只有01,逢21

书写格式:0b或者0b开头

使用场合:二进制指令\二进制文件,变量在内存中就是二进制存储

二进制和十进制的互相转换

n为二进制位所能表示的数据范围(不考虑负数):0~2n次方-1


八进制

特点:0~7,逢八进一

书写格式:0开头

八进制和二进制的互相转换


十六进制

特点:0~F,逢十六进一

书写格式:0x或者0X开头

十六进制和二进制的互相转换


总结

Mac中计算器的使用

printf以不同进制形式进行输出


示例:

#include <stdio.h>/* %d\%i  十进制形式输出整数 %c 输出字符 %p 输出地址 %f 输出小数 %o 八进制形式输出整数 %x 十六进制形式输出整数  */int main(){    // 默认情况下,就是十进制    int number = 12;        // 二进制(0b或者0B开头)    int number2 = 0B1100;        // 八进制(0开头)    int number3 = 014;        // 十六进制(0x或者0X开头)    int number4 = 0xc;        // %d以10进制整数的形式输出一个数值    printf("%x\n", number);        return 0;}



变量的内存分析

研究变量在内存中的具体存储情况

字节和地址

为了更好地理解变量在内存中的存储细节,先来认识一下内存中的字节地址

内存以字节为单位


不同类型占用的字节是不一样的


变量的存储

所占用字节数跟类型有关,也跟编译器环境有关


变量实例

int b = 10;

int a = 134;

内存由大到小寻址

只存储二进制形式

每个变量都有地址:第一个字节的地址就是变量的地址

查看内存地址的两种方式:%x%p

查看整数的二进制形式

// 输出整数的二进制形式

void putBinary(int n)

{

    int bits = sizeof(n) * 8;

    while (bits-->0) {

        printf("%d", n>>bits&1);

        if (bits%4==0)printf(" ");

    }

    printf("\n");

}

内存存储细节:



负数在内存中的存储

一个字节的取值范围 

负数的表示形式 

原码、反码、补码



类型说明符

shortlong

100l100ll100的区别

longlong long的输出

不同类型所占用的存储空间


1> shortlong可以提供不同长度的整型数,也就是可以改变整型数的取值范围。在64bit编译器环境下,int占用4个字节(32bit),取值范围是-231~231-1short占用2个字节(16bit),取值范围是-215~215-1long占用8个字节(64bit),取值范围是-263~263-1

2>总结一下:在64位编译器环境下,short2个字节(16)int4个字节(32)long8个字节(64)。因此,如果使用的整数不是很大的话,可以使用short代替int,这样的话,更节省内存开销。

3>世界上的编译器林林总总,不同编译器环境下,intshortlong的取值范围和占用的长度又是不一样的。比如在16bit编译器环境下,long只占用4个字节。不过幸运的是,ANSI \ ISO制定了以下规则:

shortint至少为16(2字节)

long至少为32(4字节)

short的长度不能大于intint的长度不能大于long

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

4>可以连续使用2long,也就是long long。一般来说,long long的范围是不小于long的,比如在32bit编译器环境下,long long占用8个字节,long占用4个字节。不过在64bit编译器环境下,long longlong是一样的,都占用8个字节。

5> 还有一点要明确的是:short int等价于shortlong int等价于longlong long int等价于long long


signedunsigned

1> 首先要明确的:signed int等价于signedunsigned int等价于unsigned

2> signedunsigned的区别就是它们的最高位是否要当做符号位,并不会像shortlong那样改变数据的长度,即所占的字节数。

signed表示有符号也就是说最高位要当做符号位所以包括正数负数和0其实int的最高位本来就是符号位已经包括了正负数和0因此signedint是一样的signed等价于signed int也等价于intsigned的取值范围是-231 ~ 231 - 1

unsigned表示无符号也就是说最高位并不当做符号位以不包括负数64bit编译器环境下面int占用4个字节32bit),因此unsigned的取值范围是0000 0000 0000 0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111 1111 1111 1111也就是0 ~ 232 - 1


示例:

#include <stdio.h>/* int 4个字节  %d short 2个字节 %d long 8个字节  %ld long long 8个字节 %lld   signed unsigned  %u */int main(){    // 0000 0000 0000 0000 0000 0000 0000 0000    // 2的31次方-1    // 2的32次方-1    int num;        /*     signed和unsigned的区别:      signed 最高位要当做符号位     unsigned 最高位不要当做符号位    */    // signed == signed int    // signed 有符号:正数、0、负数    signed int a = 10;    signed a2 = 10;        // unsigned int == unsigned    // unsigned 无符号:0、正数    unsigned int b = 10;    unsigned b2 = 10;        long unsigned int c = 34343;    long unsigned c2 = 423432;        short unsigned int d = 4343;    short unsigned d2 = 43243;        short signed int e = 54354;    short signed e2 = 434;                    return 0;}void longAndShort()


位运算

&按位与

功能

只有对应的两个二进位均为1结果位才为1否则为0

举例: 比如9&5其实就是1001&101=1因此9&5=1

规律

二进制中,与1&就保持原位,与0&就为0


| 按位或

功能

只要对应的二个二进位有一个为1结果位就为1否则为0

举例: 比如9|5其实就是1001|101=1101因此9|5=13


^ 按位异或

1> 功能

当对应的二进位相异不相同结果为1否则为0

2>举例: 比如9^5其实就是1001^101=1100因此9^5=12

3> 规律

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

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

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


~ 取反

对整数a的各二进位进行取反,符号位也取反0110


<< 左移

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

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


>> 右移

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

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

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


示例:

#include <stdio.h>int main(){    /* 按位与 &          10101010000     00000100000     -------------     00000000000          10111011     10101101     ---------     10101001          1001     0101     -----     0001     */        /*     按位或 |     1001     0101     -----     1101     */            /*     按位异或 ^      1.相同数值进行异或,结果肯定是0,比如9^9     2.交换 9^5^6 == 9^6^5     3.任何数值跟0进行异或,结果还是原来的数值,9^0 == 9     4.a^b^a == a^a^b == 0^b == b          1001     0101     -----     1100          1001     1001     -----     00000          0101     0000     ----     0101          9^5^9 == 9^9^5 = 0^5 = 5          a^b^a == b     */    //printf("%d\n", 9^9);        //printf("%d\n", 9 ^ 5);        /*     按位取反 ~     ~0000 0000 0000 0000 0000 0000 0000 1001      1111 1111 1111 1111 1111 1111 1111 0110     */    //printf("%d\n", ~9);        /*     左移 <<          0000 0000 0000 0000 0000 0000 0000 0000     00 0000 0000 0000 0000 0000 0000 100100          9<<1 -> 9 * 2的1次方 == 18     9<<2 -> 9 * 2的2次方 ==36     9<<n -> 9 * 2的n次方     */        //printf("%d\n", 9<<1);        /*     右移 >>     0000 0000 0000 0000 0000 0000 0000 0000     000000 0000 0000 0000 0000 0000 0000 10     111111 1111 1111 1111 1111 1111 1111 10           8>>1 -> 8/2 == 4     8>>2 -> 8/2的2次方 == 2     8>>n -> 8/2的n次方     */        printf("%d\n", 8>>3);        return 0;}


示例:

在不用引入其他变量的情况下,使用位异或^运算符实现两个变量值的互换

#include <stdio.h>/* 使用位异或运算符交换两个变量的值 */int main(){    int a = 10;    int b = 11;        /* 借助第三方变量    int temp = a;    a = b;    b = temp;    */        /*    a = b - a;    b = b - a;    a = b + a;    */        // a^b^a == b        // a -->  10^11    // b -->  10    a = a ^ b;    b = a ^ b;    a = a ^ b;        printf("a=%d, b=%d\n", a, b);        return 0;}

运算结果:


使用位与&运算符变量的奇偶性

#include <stdio.h>/* 用位与&运算符判断变量的奇偶性 */int main(){    /*     15: 1111     9:  1001          14: 1110     10: 1010     */    int a = 15;        a&1 == 1 ;// 奇数    a&1 == 0 ;// 偶数        /*    if (a%2) {        printf("奇数\n");    } else {        printf("偶数\n");    }*/        a%2==0?printf("偶数\n"):printf("奇数\n");        //a%2?printf("奇数\n"):printf("偶数\n");                return 0;}

运算结果:



编写一个函数,用来输出整数在内存中的二进制形式

/*写一个函数,用来输出整数在内存中的二进制形式*/#include <stdio.h>void printBinary(int number);int main(){    /*     0000 0000 0000 0000 0000 0000 0000 0000     0000 0000 0000 0000 0000 0000 0000 1111          9 : 0000 0000 0000 0000 0000 0000 0000 1001     -10 : 1111 1111 1111 1111 1111 1111 1111 0110     */            //printf("%d\n", ~9);            printBinary(-10);    return 0;}void printBinary(int number){        // 记录现在挪到第几位    // (sizeof(number)*8) - 1 == 31    int temp = ( sizeof(number)<<3 ) - 1;        while ( temp >= 0 )    {        // 先挪位,再&1,取出对应位的值        int value = (number>>temp) & 1;        printf("%d", value);                //         temp--;                // 每输出4位,就输出一个空格        if ( (temp + 1) % 4 == 0 )        {            printf(" ");        }    }        printf("\n");}

运算结果:



char类型

存储细节

ASCII单字节表(双字节GBK\GB2312\GB18030\Unicode

常见错误

char c = A;

char c ="A";

char c ='ABCD';

char c =''

当做整型使用

-128~127范围内,可以当做整数来用

%c%d\%i的使用

printf(“%d”, ‘A’);

printf(“%c”, 68);

转义字符

转义字符

意义

ASCII码值

\n

将当前位置移到下一行开头(回车换行)

10

\t

跳到下一个TAB位置

9

\\

代表一个反斜线字符

92

\'

代表一个单引号字符

39

\"

代表一个双引号字符

34

\0

空字符

0


示例:

编写一个函数,将小写字母转为大写

#include <stdio.h>char upper(char c){    // 如果是小写字母,就转成大写    /*    if (c>='a' && c<='z') { // ['a', 'z']        return c - ('a'-'A');    } else {// 如果不是小写字母,返回字母本身        return c;    }*/        // 如果是小写字母,就转成大写    if (c>='a' && c<='z') { // ['a', 'z']        return c - ('a'-'A');    }    // 如果不是小写字母,返回字母本身    return c;}int main(){    char cc = upper('f');        printf("%c\n", cc);    return 0;}

运行结果:


说出程序的输出结果

int main()

{

    int i = 67 + '4';

    char c = 'c' - 10;

    

    printf("%d - %c\n", i, i);

    printf("%d - %c\n", c, c);

    return 0;

}


运行结果:


0 0