最近在项目中遇到一个问题,当使用double类型数据时,在进行jce编解码后会出现乱数据问题,比如encode一个数据.
Encode:
{
"index": 10,
"score": 10.12,
......
}
再decode出来,会发现与原来encode进去的数据不一样,看起来像是未定义的一个值
Decode:
{
"index": 10,
"score": -1.53533e+267,
......
}
项目之前也有相同的应用场景,但是没有出现问题,所以首先怀疑jce版本是否有升级过,但发现jce版本没有被改动过,可以排除是jce的问题(实际上也是jce的问题,后面解释)。想到最近项目在编译时加了-O2的优化选项,故验证之,果然是-O2搞的鬼。但是为什么加了-O2的优化选项会触发这个bug,为了解决这个问题,需要弄清楚两点:
大概意思是说不同类型(除了相似的类型,比如int 和 unsigned int)的指针不会指向同一个内存地址,如果违反这个规则,将会出现未知情况,举个例子:
[huanghaibin33@DevTJ-todo ~/test]$ cat test_aliasing.cpp
#include <iostream>
int main()
{
int i = 0x12345678;
short *p = (short *) &i;
short tmp;
tmp = *p;
*p = *(p+1);
*(p+1) = tmp;
printf("i=%x\n", i);
return 0;
}
[huanghaibin33@DevTJ-todo ~/test]$ g++ test_aliasing.cpp -o test_aliasing
[huanghaibin33@DevTJ-todo ~/test]$ ./test_aliasing
i=56781234
[huanghaibin33@DevTJ-todo ~/test]$ g++ -O2 test_aliasing.cpp -o test_aliasing
[huanghaibin33@DevTJ-todo ~/test]$ ./test_aliasing
i=12345678
[huanghaibin33@DevTJ-todo ~/test]$ g++ -O2 -fno-strict-aliasing test_aliasing.cpp -o test_aliasing
[huanghaibin33@DevTJ-todo ~/test]$ ./test_aliasing
i=56781234
这段代码的目的是交换一个int类型的前两个字节和后两个字节,正常编译和加了-O2, -fno-strict-aliasing 选项,程序可以正常运行,但是加了-O2而不加-fno-strict-aliasing 时, 结果并不是我们预期想要的。原因是加了-O2选项,默认打开了-strict-aliasing,程序中的short *p = (short *) &i, 破坏了aliasing 规则,编译器不会认为short 型指针p 指向 整形&i 的地址,因此对p的操作不会影响到i 的结果。
至此问题比较清晰了,接下来看看jce 哪块代码违反了aliasing规则:
inline Int64 jce_htonll(Int64 x)
{
jce::bswap_helper h;
h.i64 = x;
Int32 tmp = htonl(h.i32[1]);
h.i32[1] = htonl(h.i32[0]);
h.i32[0] = tmp;
return h.i64;
}
inline Double jce_ntohd(Double x)
{
Int64 __t__ = jce_htonll((*((Int64 *)&x)));
return *((Double *) &__t__);
}
上述有两处代码违反了aliasing规则,编译出来的程序运行结果将不可知。wup已经在新版本wup-linux-c++-1.0.8.1.tgz 修复了这个bug,看看修复的代码:
inline Double jce_ntohd(Double x)
{
union helper {
Double d;
Int64 i64;
};
helper.d = x;
helper.i64 = jce_htonll( helper.i64 );
return helper.d;
}
[huanghaibin33@DevTJ-todo ~/test]$ cat test_aliasing_v2.cpp
#include <iostream>
int main()
{
union helper {
int i;
short s;
};
int i = 0x12345678;
helper h;
h.i = i;
short tmp = h.s;
h.s = *(&(h.s) + 1);
*(&(h.s) + 1) = tmp;
i = h.i;
printf("i=%x\n", i);
return 0;
}
[huanghaibin33@DevTJ-todo ~/test]$ g++ test_aliasing_v2.cpp -o test_aliasing_v2
[huanghaibin33@DevTJ-todo ~/test]$ ./test_aliasing_v2
i=56781234
[huanghaibin33@DevTJ-todo ~/test]$ g++ -O2 test_aliasing_v2.cpp -o test_aliasing_v2
[huanghaibin33@DevTJ-todo ~/test]$ ./test_aliasing_v2
i=56781234
[huanghaibin33@DevTJ-todo ~/test]$ g++ -O2 -fno-strict-aliasing test_aliasing_v2.cpp -o test_aliasing_v2
[huanghaibin33@DevTJ-todo ~/test]$ ./test_aliasing_v2
i=56781234
在存在强制类型转换的情况下,采用-O1和采用-O2或-O3产生的运行结果是不同的。在项目中应尽量避免不同类型的指针转换,使用编译优化选项时要多加重视编译告警。
http://km.oa.com/group/578/articles/show/150732?kmref=search&from_page=1&no=1
https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。