eyasliu / blog

博客文章
179 stars 13 forks source link

了解一下字节码 #30

Open eyasliu opened 4 years ago

eyasliu commented 4 years ago

字节码编程

字节码,在编程中无处不在,但是在业务层中也许您也用不着,但是了解一下还是有好处的。开发稍微底层一些的逻辑基本都会碰到

常识与基本概念

我们都听过一些概念:

深入

编程语言的类型

var num int = 0X14

比如C语言的 int 类型,它是 32 位的,也就是 4 个字节,一个int类型数字需要占用 4 个内存格子,那么放在内存应该是这样存储的(字节之间没有空格,这里只是为了好看才加的空格)

0X00 00 00 14

转成字节(8进制)

0B0000 0000 0000 0024

所以还是转成16进制好看些,我们看到的内存地址也通常是16进制的。

大序端与小序端

将高位数的地址放在前面还是后面,这成了个问题,可能以前做计算机的大佬们没商量好,就导致有了大序端和小序端。有点类似于文字的方向 ltr 和 rtl,也就是文字是从左往右念,还是从右往左念,大部分语言是从左往右的了,比如中文和英文。

另外也叫大端法、小端法,或者大端对齐、小端对齐,其实说的都是一个概念。在网络传输中有规定,必须要使用大序端进行传输,于是在网络这么发达的今天,大序端是主流。小序端比较少遇到,遇到的时候麻烦您再转一次吧

后文的所有示例,都基于大序端

操作技巧

类型转换

大部分编程语言都会有 byte 或者 uint8 这个类型,它的一个值就是一个字节,它是跟内存中的字节对等的,又因为所有的运行中的值都是存在内存中的,所以字节数组可以用来表示所有数据,那是不是字节数组可以和所有的数据类型进行转换呢,可以,但是这前提是理解编程语言内部的序列和反序列化才能这么干,实际上即使知道理解了也不会那么干。

但是对于常用的数据类型和字节数组的转换我们还是得需要知道如何转换的。

数字转字节数组 []byte

数字的转换其实上文作为例子已经展示过了,总结一下流程

  1. 确定该数字类型的长度,每 8 位一个字节,如 int32 的字节数组长度是 4 ,int64 是的字节数组长度是 8
  2. 把数字换算成16进制,回填到低位字节位,其余字节位用0补齐数组长度

如把 int32 类型的 20 数字转成字节数组

20 = 0X00000014
bytes = [4]byte{0X00,0X00,0X00,0X14}

事实上,这种计算方法是很 low 的,更好的方法应该是使用位移才对。

字符串转字节数组 []byte

字符串是有编码的,常用的编码有 ascii, utf8, gbk 等等,通常推荐只用 utf8, 在学习字符串转字节数组前,我们先来了解下什么是字符串编码吧。

字符编码

简单的解释就是一个映射表(码表),一个id(也称:码点、码位)对应了一个字符,不同编码的不同点在于他们的映射表有多大,占用多少空间,仅此而已。

提到了 utf8,那就不得不说一下 Unicode 了。

他们的关系有点像是接口和实现类,但又不是那么回事。


再继续看看字符转字节数组的操作,这个其实没什么好说的,按照码表对照来转就是了,比如 utf8 编码的 根据码表查到就是 0Xe6b7a6,转成字节数组 []byte{e6, b7, a6},它的 Unicode 码点是 \u6DE6

字符串转字节数组同理,就是多个字符串的转换结果拼接起来。通常不需要我们自己去计算,按理说标准库应该都直接有集成,还是直接用标准库吧。

// Golang 例子
str := "你说得对"
bytes := []byte(str)

JavaScript 中的字节码

JS 有没有 byte 类型?没有。但它有 ArrayBuffer,但是 ArrayBuffer 它是用来存储数据的,它的值无法修改,无法给它赋值,需要转成 TypedArray 或者 DataView 才能修改。TypedArray 和 DataView 的用途有些不一样

TypedArrayDataView 实例有什么方法我就不多说,直接看文档来得实在,这里提供几个例子

数值类型转ArrayBuffer

JavaScript 的类型中,它只有 Number 这个数据类型,它的类型长度其实我不知道,但是我知道它最大能安全处理的数字是 2^53 - 1,好像长度是 53,超过这数字处理不了,但是也还能存储大致的值不会报错,所以,千万别用 64 位去存储js的数字,那是不可靠的,我建议把js的number转成4个字节(即32位)。

function numToBuffer(num) {
    const buffer = new ArrayBuffer(4) // 4个字节长度,即 32 位
    const view = new DataView(buffer) // ArrayBuffer 不能直接编辑,只能转换成 DataView 或者 TypedArray 才能编辑,这里是处理数值,所以用 DataView
    view.setInt32(0, num) // 从第0个字节开始填充 num 以 int32 类型转成的字节
    return view.buffer // 再把 DataView 转成 ArrayBuffer
}

字符串转 ArrayBuffer

还记得当时字符串转字节数组说要根据字符编码吗,js的字符串默认是 Unicode 编码,得需要转成 utf8 编码。怎么转呢?还是用开源库吧,这个: encode-utf8 ,它还有个对应的utf8解码库: decode-utf8

Golang 中的字节码

Golang 的基础类型 byteuint8 他们是等价的

type byte = uint8

所以字节数组就是 []byte

数值类型 和 []byte 互转

使用标准库做类型转换

import (
    "bytes"
    "encoding/binary"
)

func IntToByteArray(num int64) []byte {
    buf := new(bytes.Buffer)
    binary.Write(buf, binary.BigEndian, num)
    return buf.Bytes()
}

func ByteArrayToInt(arr []byte) int64 {
    data := binary.BigEndian.Uint64(arr)
    return int64(data)
}

字符串 与 []byte 互转

这个写过go的应该没有人不知道了,不啰嗦了

总结

字节码操作在编程过程中是非常重要的技能点,用不用得着另外说,学会了至少能装逼,会让人不明觉厉。