我正在开发一款适用于x86的Android应用程序,需要与C进行一些集成。我一直在使用swig / JNI来完成这项工作,而且大部分时间都在顺利运行。但是,指针一直给我一些错误。
我的问题是我能够在模拟器(ARM)中成功引用变量地址,但在设备(x86)上,事情并不顺利。
使用this link中的示例,我发现一旦该地址转移到Java,C中任何已分配变量的地址都将变为NULL。例如......
Swig生成的JNI:
SWIGEXPORT jlong JNICALL Java_exampleJNI_new_1intp(JNIEnv *jenv, jclass jcls) {
jlong jresult = 0 ;
int *result = 0 ;
(void)jenv;
(void)jcls;
result = (int *)new_intp();
LOGI("Result is %x", result);
*(int **)&jresult = result;
LOGI("JResult is %x", jresult);
return jresult;
}
包含new_intp()的源文件:
static int *new_intp() {
return (int *) calloc(1,sizeof(int));
}
我有print语句检查地址的值,因为它来自C并传递给Java。在new_intp()中,新变量被分配了一个外观漂亮的地址,但是一旦这个值返回到JNI并被转换为jlong,它就会变成NULL。
换句话说,*(int **)&jresult = result;
导致jresult为0.
为什么会这样?是否有一些特殊性的x86不允许JNI使用指针?或者是因为我在物理设备而不是模拟器上测试它?
此致
答案 0 :(得分:11)
实际上,这是一个pointer-aliasing问题。 SWIG正在使用老式的C指针技术,这些技术在优化开启时不适用于较新的GCC。在SWIG文档中专门埋藏says what to do:
重要强>
如果您打算使用gcc启用优化功能 (例如-O2),确保您还使用-fno-strict-aliasing进行编译。 从gcc-4.0开始,GCC的优化变得更加激进 并将导致代码失败并具有严格的别名优化 打开。有关详细信息,请参阅C/C++ to Java typemaps部分。
答案 1 :(得分:2)
在我看来它可能是一个字节序问题。
*(int **)&jresult = result;
如果int
是32位,jresult
是64位,那么在大端架构上,这可能会产生意想不到的结果。
&jresult
是指向64位值的指针,因此如果将其转换为指向32位值的指针,那么您将指向两个组成32位的低位地址话。在大端系统中,这将是最重要的词。因此,在将32位字写入64位值的最高端后,您将获得64位值,这比您预期的大2 ^ 32倍。
如果您的LOGI
调用将参数视为32位int,并从64位值隐式向下转换,则它将给出0。
你能看到这是否有效吗?
jresult = (jlong) result;