Open eyasliu opened 4 years ago
字节码,在编程中无处不在,但是在业务层中也许您也用不着,但是了解一下还是有好处的。开发稍微底层一些的逻辑基本都会碰到
我们都听过一些概念:
0B00010100
024
0X14
20
上面这些例子中,有些单位是字节(byte),有些是 位(bit),这个 位指的是比特位,也就是二进制位, 8 个比特位等于 1 个字节 8bit = 1byte,为什么大多数情况要把比特换算成字节来表示呢?因为计算机中的内存条的最小单元就是字节。可以理解成内存条是有一个个格子组成的,每个格子存储一个字节,也就是 8 个比特位。每个格子都有它的唯一编号,称为内存地址。
字节
位
8bit = 1byte
当操作系统是32位时,最多能标记 2^32 个格子,也就是 4GB,当操作系统是64位时,最多能标记 2^64个格子,也就是 17179869184GB。所以说32位的系统的内存最多只能有4GB,推荐现在的所有操作系统都升级到 64 位的。
2^32
2^64
另外在某些编程语言中,如Go, C, int 类型的长度是和操作系统有关的,在32位系统上int是32位,在64位系统上int是64位的,当然有些编程语言是固定长度的,如java的int永远都是32位,下文为了统一口径,将 int 类型都视为 32 位
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
uint8
但是对于常用的数据类型和字节数组的转换我们还是得需要知道如何转换的。
数字的转换其实上文作为例子已经展示过了,总结一下流程
如把 int32 类型的 20 数字转成字节数组
20 = 0X00000014 bytes = [4]byte{0X00,0X00,0X00,0X14}
事实上,这种计算方法是很 low 的,更好的方法应该是使用位移才对。
字符串是有编码的,常用的编码有 ascii, utf8, gbk 等等,通常推荐只用 utf8, 在学习字符串转字节数组前,我们先来了解下什么是字符串编码吧。
简单的解释就是一个映射表(码表),一个id(也称:码点、码位)对应了一个字符,不同编码的不同点在于他们的映射表有多大,占用多少空间,仅此而已。
提到了 utf8,那就不得不说一下 Unicode 了。
他们的关系有点像是接口和实现类,但又不是那么回事。
再继续看看字符转字节数组的操作,这个其实没什么好说的,按照码表对照来转就是了,比如 utf8 编码的 淦 根据码表查到就是 0Xe6b7a6,转成字节数组 []byte{e6, b7, a6},它的 Unicode 码点是 \u6DE6
淦
0Xe6b7a6
[]byte{e6, b7, a6}
\u6DE6
字符串转字节数组同理,就是多个字符串的转换结果拼接起来。通常不需要我们自己去计算,按理说标准库应该都直接有集成,还是直接用标准库吧。
// Golang 例子 str := "你说得对" bytes := []byte(str)
JS 有没有 byte 类型?没有。但它有 ArrayBuffer,但是 ArrayBuffer 它是用来存储数据的,它的值无法修改,无法给它赋值,需要转成 TypedArray 或者 DataView 才能修改。TypedArray 和 DataView 的用途有些不一样
ArrayBuffer
TypedArray
DataView
TypedArray 和 DataView 实例有什么方法我就不多说,直接看文档来得实在,这里提供几个例子
数值类型转ArrayBuffer
JavaScript 的类型中,它只有 Number 这个数据类型,它的类型长度其实我不知道,但是我知道它最大能安全处理的数字是 2^53 - 1,好像长度是 53,超过这数字处理不了,但是也还能存储大致的值不会报错,所以,千万别用 64 位去存储js的数字,那是不可靠的,我建议把js的number转成4个字节(即32位)。
2^53 - 1
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 的基础类型 byte 和 uint8 他们是等价的
type byte = uint8
所以字节数组就是 []byte
[]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的应该没有人不知道了,不啰嗦了
字节码操作在编程过程中是非常重要的技能点,用不用得着另外说,学会了至少能装逼,会让人不明觉厉。
字节码编程
字节码,在编程中无处不在,但是在业务层中也许您也用不着,但是了解一下还是有好处的。开发稍微底层一些的逻辑基本都会碰到
常识与基本概念
我们都听过一些概念:
0B00010100
这是一个二进制数字(2^4 + 2^2),024
这是一个8进制的数字(8^1 2 + 8^0 4),0X14
这是一个16进制数字(16^1 1 + 16^0 4),他们都是表示十进制的20
上面这些例子中,有些单位是
字节
(byte),有些是位
(bit),这个位
指的是比特位,也就是二进制位, 8 个比特位等于 1 个字节8bit = 1byte
,为什么大多数情况要把比特换算成字节来表示呢?因为计算机中的内存条的最小单元就是字节。可以理解成内存条是有一个个格子组成的,每个格子存储一个字节,也就是 8 个比特位。每个格子都有它的唯一编号,称为内存地址。当操作系统是32位时,最多能标记
2^32
个格子,也就是 4GB,当操作系统是64位时,最多能标记2^64
个格子,也就是 17179869184GB。所以说32位的系统的内存最多只能有4GB,推荐现在的所有操作系统都升级到 64 位的。另外在某些编程语言中,如Go, C, int 类型的长度是和操作系统有关的,在32位系统上int是32位,在64位系统上int是64位的,当然有些编程语言是固定长度的,如java的int永远都是32位,下文为了统一口径,将 int 类型都视为 32 位
深入
编程语言的类型
比如C语言的 int 类型,它是 32 位的,也就是 4 个字节,一个int类型数字需要占用 4 个内存格子,那么放在内存应该是这样存储的(字节之间没有空格,这里只是为了好看才加的空格)
转成字节(8进制)
所以还是转成16进制好看些,我们看到的内存地址也通常是16进制的。
大序端与小序端
将高位数的地址放在前面还是后面,这成了个问题,可能以前做计算机的大佬们没商量好,就导致有了大序端和小序端。有点类似于文字的方向 ltr 和 rtl,也就是文字是从左往右念,还是从右往左念,大部分语言是从左往右的了,比如中文和英文。
另外也叫大端法、小端法,或者大端对齐、小端对齐,其实说的都是一个概念。在网络传输中有规定,必须要使用大序端进行传输,于是在网络这么发达的今天,大序端是主流。小序端比较少遇到,遇到的时候麻烦您再转一次吧
后文的所有示例,都基于大序端
操作技巧
类型转换
大部分编程语言都会有
byte
或者uint8
这个类型,它的一个值就是一个字节,它是跟内存中的字节对等的,又因为所有的运行中的值都是存在内存中的,所以字节数组可以用来表示所有数据,那是不是字节数组可以和所有的数据类型进行转换呢,可以,但是这前提是理解编程语言内部的序列和反序列化才能这么干,实际上即使知道理解了也不会那么干。但是对于常用的数据类型和字节数组的转换我们还是得需要知道如何转换的。
数字转字节数组 []byte
数字的转换其实上文作为例子已经展示过了,总结一下流程
如把 int32 类型的
20
数字转成字节数组字符串转字节数组 []byte
字符串是有编码的,常用的编码有 ascii, utf8, gbk 等等,通常推荐只用 utf8, 在学习字符串转字节数组前,我们先来了解下什么是字符串编码吧。
字符编码
简单的解释就是一个映射表(码表),一个id(也称:码点、码位)对应了一个字符,不同编码的不同点在于他们的映射表有多大,占用多少空间,仅此而已。
提到了 utf8,那就不得不说一下 Unicode 了。
他们的关系有点像是接口和实现类,但又不是那么回事。
再继续看看字符转字节数组的操作,这个其实没什么好说的,按照码表对照来转就是了,比如 utf8 编码的
淦
根据码表查到就是0Xe6b7a6
,转成字节数组[]byte{e6, b7, a6}
,它的 Unicode 码点是\u6DE6
字符串转字节数组同理,就是多个字符串的转换结果拼接起来。通常不需要我们自己去计算,按理说标准库应该都直接有集成,还是直接用标准库吧。
JavaScript 中的字节码
JS 有没有 byte 类型?没有。但它有
ArrayBuffer
,但是 ArrayBuffer 它是用来存储数据的,它的值无法修改,无法给它赋值,需要转成TypedArray
或者DataView
才能修改。TypedArray 和 DataView 的用途有些不一样TypedArray
和DataView
实例有什么方法我就不多说,直接看文档来得实在,这里提供几个例子数值类型转ArrayBuffer
JavaScript 的类型中,它只有 Number 这个数据类型,它的类型长度其实我不知道,但是我知道它最大能安全处理的数字是
2^53 - 1
,好像长度是 53,超过这数字处理不了,但是也还能存储大致的值不会报错,所以,千万别用 64 位去存储js的数字,那是不可靠的,我建议把js的number转成4个字节(即32位)。字符串转 ArrayBuffer
还记得当时字符串转字节数组说要根据字符编码吗,js的字符串默认是 Unicode 编码,得需要转成 utf8 编码。怎么转呢?还是用开源库吧,这个: encode-utf8 ,它还有个对应的utf8解码库: decode-utf8
Golang 中的字节码
Golang 的基础类型
byte
和uint8
他们是等价的所以字节数组就是
[]byte
数值类型 和 []byte 互转
使用标准库做类型转换
字符串 与 []byte 互转
这个写过go的应该没有人不知道了,不啰嗦了
总结
字节码操作在编程过程中是非常重要的技能点,用不用得着另外说,学会了至少能装逼,会让人不明觉厉。