通过union进行类型转换

0x01.起因

OpenResty群里抛出一个如下的问题

===========技术点,征解决代码
如何把网络传输的 float 数字转换成 lua 可以识别的 number ?
float 类型,在网络字节流中是 0x00 0x00 0xFB 0x42 ,它所对应的实际值是 125.5 。请问如果在 lua 中获取到字节流后,如何把他转成我们需要的 125.5 ?
同样的,double 类型也需要类似代码。

这玩意看着挺简单,想着通过ffi,搞个atof不就可以了吗?自己试了下发现,结果不对,一直是0。过了一会一位同学直接抛出了代码,最后说了句 “union做类型转换,最好用了。。”, 由于咱C语言学的不太好,根本没听过这方法啊,于是Google了一番,也算是记个笔记。

0x02. C语言实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
typedef union _UnFloat {
float value;
char buff[4];
}UnFloat;
int main() {
UnFloat uf;
uf.value = 0;
char cf[4]={0x00, 0x00, 0xFB, 0x42};
memcpy(uf.buff,cf,sizeof(uf.buff));
printf("The union converted float value is %f", uf.value);
getchar();
return 0;
}

0x03. luajit代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local ffi = require 'ffi'
ffi.cdef [[
typedef union {
char buf[4];
float f;
} point_t;
]]
local arr = {0x00, 0x00, 0xFB, 0x42}
local str = ''
for _,v in ipairs(arr) do
str = str .. string.char(v)
end
local v = ffi.new("point_t", str)
print(v.f)

0x04. 总结

该方法确实挺好用,atof不能处理这种数据,另外补充一下lua的string如何转换成c_str

1
2
3
4
5
6
7
方法1
local str = "test"
local c_str = ffi.new("char [?]", #str)
ffi.copy(c_str, str)
方法2
local c_str = ffi.new("const char *", test)

反之通过ffi.string即可