当前位置:   article > 正文

深入理解计算机系统(2)_0x61十六进制表示多少

0x61十六进制表示多少

第二章 信息的表示和处理



对于二进制所表示的数字来说,主要有三种,即无符号、补码以及浮点数。计算机的表示法是用有限数量的位来对一个数字编码,因此,当结果太大以至不能表示时,某些运算就会溢出。溢出会导致某些令人吃惊的后果。例如,int类型使用四个字节(32位二进制)来表示,计算表达式200300400*500会得出-884901888,这违背了数学运算特性。





2.1 信息存储


大多数计算机使用8位的字节,作为最小的可寻址的内存单位,而不是访问内存中单独的位。换句话说,我们在访问内存的内容时,最小的访问单位是字节。机器级程序将内存抽象为一个非常大的字节数组,称为虚拟内存。内存的每个字节都由一个唯一的数字来标识,称为它的地址,数组下标是地址的增量,所有可能地址的集合就称为虚拟地址空间。虚拟地址空间是为了给机器级的程序一个概念上的映像,实际的实现是将动态随机访问存储器(DRAM)、闪存、磁盘存储器、特殊硬件和操作系统软件结合起来,为程序提供一个看上去统一的字节数组。



2.1.1 十六进制表示法


对于机器来说,可能比较喜欢0和1,但是对于人类这种高级生物来说,1和0就有点不够看了。因此通常情况下,为了便于阅读,我们会使用十六进制去表示二进制。1位十六进制的数字可以表示4位二进制数字,因此一个字节就可以表示为0x00—0xFF


  • 十六进制和二进制之间的相互转换

在这里插入图片描述

  • 十六进制和十进制之间的相互转换

1)十进制转十六进制:用十进制数不断的除以16,得到一个商和一个余数,余数用十六进制数表示作为最低位数。以此类推。例如:十进制数314156.
314156 ÷ 16 = 19634・・・・12 (C)
19634 ÷ 16 = 1227 ・・・・ 2 (2)
1227 ÷ 16 = 76 ・・・・11 (B)
76 ÷ 16 = 4 ・・・・12 (C)
4 ÷ 16 = 0 ・・・・ 4 (4)
所以十六进制数为0x4CB2C。

2)十六进制转十进制:用16的幂乘以每个十六进制数。例如:0x7AF.
7 x 16^2 + 10 x 16^1 + 15 x 16^0 = 1792 + 160 + 15 = 1976.
所以十进制数为1976



2.1.2 寻址和字节顺序


对于跨越多字节的程序对象,我们必须建立两个规则才能唯一确定一个程序对象的值:
1)这个对象的起始虚拟内存地址是多少?
2)以及在内存中此对象字节的排列顺序是什么(比如int类型4个字节的排列顺序是什么)?
对于第一个问题,由于大部分计算机都采用连续的内存地址去存储一个程序对象,因此我们称内存地址中最小的那个就是该程序对象的起始地址,也是该程序对象的地址。例如,假设一个类型为int的变量x的地址为0x100,也就是说,地址表达式&x的值为0x100,那么,(假设数据类型int为32位表示)x的4个字节将被存储在内存的0x100、0x101、0x102和0x103位置。
对于第二个问题,一般有两种方式,即大端法和小端法。

  • 小端存储:按照从最低有效字节到最高有效字节的顺序存储
  • 大端存储:按照从最高有效字节到最低有效字节的顺序存储
    例如:
    在这里插入图片描述

那么Java中如何确定当前环境所用的CPU是何种类型的字节顺序呢?
在java.nio.Bits中是这样实现的:

static {
   
        long a = unsafe.allocateMemory(8);
        try {
   
            unsafe.putLong(a, 0x0102030405060708L);
            byte b = unsafe.getByte(a);
            switch (b) {
   
            case 0x01: byteOrder = ByteOrder.BIG_ENDIAN;     break;
            case 0x08: byteOrder = ByteOrder.LITTLE_ENDIAN;  break;
            default:
                assert false;
                byteOrder = null;
            }
        } finally {
   
            unsafe.freeMemory(a);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

原理很简单,就是先分配8个字节Long类型的内存,然后放内存中放入16进制0x0102030405060708L的数据,判断第一个字节是0x01还是0x08,如果是0x01,则说明大端字节顺序,如果是最大的0x08,则是小端字节顺序。



2.1.3 强制类型转转换


对于一个特定的数据类型来讲,计算机在解释这类数据的值的时候,是根据起始位置以及数据类型的位数来确定的。比如对于无符号int类型的数据来说,倘若我们知道它的起始位置为0x1,而当前的操作系统采取的是大端法规则,假设0x1-0x4的内存地址中存储的字节依次为0xFF,0xFF,0xFF,0xFF,由此计算机将会帮我们计算出这个无符号int类型的值为232-1,也就是无符号int类型的最大值


这其中计算机是根据0x1-0x4这四个字节上的值,以及无符号int类型的解释方式,最终得到的这个无符号int类型的值。


由此可见,计算机在解释一个数据类型的值时主要有四个因素:

  1. 位排列规则(大端或者小端)
  2. 起始位置
  3. 数据类型的字节数
  4. 数据类型的解释方式

对于特定的系统来说,前两种因素都是特定的,而对于后两种因素的改变,则可以改变一个数据类型的值的最终计算结果,这就是强制类型转换。对于大部分高级程序设计语言来讲,都提供了强制类型转换。


比如C语言,我们可以将一个无符号int类型的值强制转换为其它类型,在转换之后,对于上面四个因素之中,改变的是最后两个。

#include <stdio.h>

int main(){
   
    unsigned int x = 0xFFFFFF61;
    int *p = &x;
    char *cp = (char *)p;
    printf("%c\n",*cp);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这是一个简单的强制类型转换示例,可以看到我们将一个无符号int类型的值,先赋给了一个int类型的指针,又强制转换成了char类型的指针,最终我们输出这个char类型指针所代表的字符。
结果的输出是一个a


输出a的原因就是由上面的四个因素决定的,我们看这个具体程序上的四个因素。


  1. cp指针的值与x变量的起始内存地址相等。(起始位置)
  2. 使用的linux系统是小端表示法,也就是说假设x变量的起始内存地址为0x1,那么0x1-0x4的值分别为0x61、0xFF、0xFF、0xFF。(位排列规则)
  3. char只占一个字节,因此会只读取0x61这个值。(数据类型的字节数,或者说大小)
  4. 0x61为十进制的97,对应ascii表的话,代表的是字符a,因此最终输出了a。(数据类型的解释方式)

可以看出,强制类型转换有时候会让结果变的让人难以预料,因此这种技巧一般不太推荐使用,但是这种手段也确实是程序设计语言所必需的。



2.1.4 表示字符串


这一点其实上面我们已经提到了,我们知道9

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/编程探险家/article/detail/63214
推荐阅读
相关标签
  

闽ICP备14008679号