数据在内存中的存储(1)

目录:

1.整数在内存中的存储

2.⼤⼩端字节序和字节序判断

正文:

1.整数在内存中的存储

整数的2进制表⽰⽅法有三种,即原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表⽰“负”,⽽数值位最 ⾼位的⼀位是被当做符号位,剩余的都是数值位。

正整数的原、反、补码都相同。

负整数的三种表⽰⽅法各不相同。

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码。

对于整形来说:数据存放内存中其实存放的是补码。 (原因:在计算机系统中,数值⼀律⽤补码来表⽰和存储。 原因在于,使⽤补码,可以将符号位和数值域统⼀处理; 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。

2.⼤⼩端字节序和字节序判断

#include <stdio.h>
int main()
{
    int a = 0x11223344;

    return 0;
}

在这段代码中,我们调试可发现:

我们第一感觉可能是感觉有点别扭,它为什么要倒着放? 但其实也有正着放的,而倒着放的叫做小端,正着放的叫大端,接下来就是要了解什么是大端和小端:

⼤端(存储)模式:是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。

⼩端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。

那么如果让你写一个代码来判断编译器是哪种储存模式,又该怎么写呢?

代码:

详解:

创建一个整形a,给其赋值为1,如果是大端储存,a在内存中就是以00 00 00 01,这里用16进制数字表示,看起来更加方便,而小端储存就是01 00 00 00。再创建一个char类型的变量b,使b = a;这样a的第一个字节的数字就会被赋值到b中,然后打印b,如果是大端,b的结果就是0,如果是小端b就是1。

练习1:

#include <stdio.h>
int main()
{
            char a = -1;
          signed char b = -1;
           unsigned char c = -1;
            printf("a=%d,b=%d,c=%d", a, b, c);     
    return 0;
}

答案:a=-1,b=-1,c=255

详解:-1的原码:1000 0000 0000 0000 0000 0000 0000 0001

                  反码:1111 1111 1111 1111 1111 1111 1111 1110

                  补码:1111 1111 1111 1111 1111 1111 1111 1111

把-1赋给a,a的类型是char,char的大小是1个字节 = 8个比特位,为1111 1111,(char是signed char,还是unsigned char取决于编译器,在vs中是signed char), 1111 1111整形提升后为1111 1111 1111 1111 1111 1111 1111 1111,而1111 1111 1111 1111 1111 1111 1111 1111的原码为1000 0000 0000 0000 0000 0000 0000 0001,,十进制为-1

b与a同理,也为-1;

而c是unsigned char,所以编译器会将1111 1111 1111 1111 1111 1111 1111 1111视为正数,而正数的原码,反码和补码相同,转换位十进制数字为255。

练习2:

#include <stdio.h> 
int main()
{    char a = -128;
     printf("%u\n",a);
     return 0; 
}

答案:4294967168

详解:-128的补码:1000 0000 0000 0000 0000 0000 1000 0000

a是char类型变量,占8个比特位,1000 0000,整形提升后为1111 1111 1111 1111 1111 1111 1000 0000,%u是无符号整形的占位符,所以将1111 1111 1111 1111 1111 1111 1000 0000视为正数,十进制为4294967168。

练习3:

#include <stdio.h>
#include<string.h>
int main() 
{    char a[1000]; 
     int i;
     for(i=0; i<1000; i++)
     {
            a[i] = -1 - i; 
     }    
     printf("%d", strlen(a)); 
     return 0; 
}

答案:255

详解:

 我们可以换个角度理解,如图:

a[0] = -1 ,a[1] = -2, a[2] = -3……......-128       127.............. 0      -1        -2........

strlen函数遇到'\0'停止,而'\0'的ASCLL码值==0,所以会在第一个0处停止,由上可知strlen(a)==255。

练习4:

#include <stdio.h>
int main() 
{    unsigned int i;   
for(i = 9; i >= 0; i--) 
{        printf("%u\n",i);    }  
return 0; 
}

此代码是无限循环,问题在于 i 被定义为 unsigned int 类型,而循环条件 i >= 0 使用了无符号整数。在 C 语言中,无符号整数永远不会小于零,因此当 i 递减到 0 后,下一次循环 i 将变为一个非常大的正数,而不是 -1。

练习5:

//在小端储存模式中
#include <stdio.h> 
int main() 
{    int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
 }

答案:4,200000

详解:在我们的深入理解指针的博客中提到过,&a是取出整个数组的地址,所以:

而ptr1[-1]等价于*(ptr1 - 1),ptr1是int*类型的指针,-1向后移动四个字节,指向4的地址;

而在(int *)((int)a + 1)中,,a被强制转换为了int类型,假设a数组的首元素地址是0x01ff1120,加一之后就变成了0x01ff1121,如图:

 打印*ptr2,从图上的位置开始往后四个字节,也就是0x02 00 00 00,打印出来就是200000。

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值