-
字节序 与 字节对齐
-
java基础类型与字节类型转换
-
2.1 struct ==》 字节类型
-
2.2 基础类型 ==》 字节类型
-
2.3 字节类型 ==》 基础类型
-
-
python使用ctypes调用c处理字节
1.1 字节序
-
大端字节序(Big-Endian):高位字节在内存的低地址,低位字节在内存的高地址,这是人类读写数值的方法。
-
小端字节序(Little-Endian):低位字节在内存的低地址,高位字节在内存的高地址,计算机的内部处理大部分是小端字节序。计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。
1 |
|
在C语言中,默认是小端(但在一些对于单片机的实现中却是基于大端,比如Keil 51C),Java是平台无关的,默认是大端。在网络上传输数据普遍采用的都是大端。
1.2 字节对齐
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float类型,其自身对齐值为4,对于double型,其自身对齐值为8,单位字节。
需要字节对齐的根本原因在于CPU访问数据的效率问题.
gcc中结构体默认是4个字节对齐,即为32的倍数
先看个简单的例子(32位,X86处理器,GCC编译器):
1 | struct A{ |
结果是:sizeof(strcut A)值为8;sizeof(struct B)的值却是12。
结构体A中包含一个4字节的int数据,一个1字节char数据和一个2字节short数据;B也一样。按理说A和B大小应该都是7字节。之所以出现上述结果,就是因为编译器要对数据成员在空间上进行对齐。
对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:
-
数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
-
联合 :按其包含的长度最大的数据类型对齐。
-
结构体: 结构体中每个数据类型都要对齐。
__attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。
如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
__attribute__ ((packed)),取消结构在编译过程中的优化对齐,
按照实际占用字节数进行对齐。
2.1 java 使用struct的ByteBuffer转换为字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DemoStruct extends Struct {
Unsigned8 a = new Unsigned8();
Unsigned16 b = new Unsigned16();
Unsigned32 c = new Unsigned32();
UTF8String d = new UTF8String(12);
/**
* 取消优化对齐
*/
public boolean isPacked() {
return true;
}
/**
* 设置为小端数据
*/
public ByteOrder byteOrder() {
return ByteOrder.LITTLE_ENDIAN;
}
public byte[] getBytes(){
this.isPacked();
ByteBuffer buff = this.getByteBuffer();
buff.order(this.byteOrder());
byte[] bytes = new byte[buff.capacity()];
IntStream.range(0, buff.capacity()).forEach(i -> bytes[i] = buff.get(i));
// Base64.getEncoder().encodeToString(bytes)
return bytes;
}
}
- 这里使用struct的无符号类型(unsigned),然后用ByteBuffer获取对应位置的字节,存储到byte[] 中。
- 但对于需要动态生成struct的地方不太适合,于是使用下面的方法依次获取数据的字节数组,然后按照顺序连接为一个byte[]。
1 | public class DemoStruct extends Struct { |
2.2 基础类型 转换 byte[]
- String 转换byte[],可以使用String的getBytes()
1 | public static byte[] getStringBytes(String data,int length){ |
- 对于其他没有getBytes()方法的基本数据类型,例如:大端序 int转换byte[]
1 | public static byte[] getIntBytes(int data) { |
- java文档中java.nio.ByteBuffer 有putInt,putChar,putShort,putFloat,putDouble,putLong等方法,但在使用过程中遇到问题:
对于定义的unsigned32的数据,需要获取4个字节的byte[]. unsigned32最大值(2^32)-1 =4294967295大于int的取值范围(-(2^31 ),(2^31 )-1),于是转换为Long使用putLong.但putLong默认写入8个byte. 当ByteBuffer.allocate(4),再putLong时报错 buffer overflow exception。
1 | public abstract ByteBuffer putLong(long value) |
于是采用二进制移位运算:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 小端序
//内存的低地址 存储 数据的低位字节
// 则bytes[0] 存储 data & (1111 1111)
// bytes[1] 存储 data 右移8位 再 &1111 1111
public static byte[] getLongBytesBig(long data,int length) {
// 设置length=4
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
bytes[i] = (byte) ((data >> (i*8)) & 0xff);
}
return bytes;
}
//大端序
public static byte[] getLongBytesLittle(long data,int length) {
// 设置length=4
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
bytes[i] = (byte) ((data >> ((length-1-i)*8)) & 0xff);
}
return bytes;
}
1 | // 小端序 |
2.3 字节流 转换java 基本数据类型
2.3.1 使用ByteBuffer自带的方法
1 | public static int getInt(byte[] bytes) { |
2.3.2 移位运算
1 | public static int getInt(byte[] bytes) { |
- python 使用ctypes 调用c 处理字节
- demodata.c
1 |
|
在linux上编译,gcc -shared -Wl,-soname,demodata -o demodata.so -fPIC demodata.c
1 | # coding=utf-8 |