目前我在处理 rust ffi 的一些问题,有一个诉求是希望在 rust 侧判断 c 传来的指针是否是零指针。
我使用 Mac 进行测试,rustc 1.48.0(最新的 nightly 也是一样的), 目前遇到了一个比较奇怪的例子。我这里提供了一个 demo:
#include "stdlib.h"
#include <stdint.h>
typedef void (*PFNStop)();
PFNStop test_c() {
return (void*) 0;
}
extern crate libc;
pub type CCallbackType = extern "C" fn();
extern "C" {
fn test_c() -> CCallbackType;
}
fn main() {
let output: CCallbackType = unsafe { test_c() };
println!("c_func_ptr_inner is: {:?} equal 0 {:?}", output as u32, output as u32 == 0);
println!("c_func_ptr_inner is: {:?} equal 0 {:?}", output as i32, output as i32 == 0);
println!("c_func_ptr_inner is: {:?} equal 0 {:?}", output as u64, output as u64 == 0);
println!("c_func_ptr_inner is: {:?} equal 0 {:?}", output as i64, output as i64 == 0);
println!("c_func_ptr_inner is: {:?} equal 0 {:?}", output as u128, output as u128 == 0);
}
extern crate cc;
fn main() {
cc::Build::new()
.file("src/double.c")
.compile("libdouble.a");
}
cargo run --release
打出来的结果为:
c_func_ptr_inner is: 0 equal 0 true
c_func_ptr_inner is: 0 equal 0 true
c_func_ptr_inner is: 0 equal 0 false
c_func_ptr_inner is: 0 equal 0 false
c_func_ptr_inner is: 0 equal 0 false
debug 模式下倒是符合预期的:
c_func_ptr_inner is: 0 equal 0 true
c_func_ptr_inner is: 0 equal 0 true
c_func_ptr_inner is: 0 equal 0 true
c_func_ptr_inner is: 0 equal 0 true
c_func_ptr_inner is: 0 equal 0 true
我对于 release 模式下的这种表现,感到非常奇怪(明明是 0 却不等于 0),想知道有没有了解的同学可以帮忙解释一下?
1
共 4 条评论, 1 页
评论区
写评论在文档里说了的,FFI的可空指针,用Option来表示。
这个问题,很有意思。 不过指针到整数的转换,这个部分如果是明确unsafe的,会感觉好一些, 可以再试一下raw pointer时,is_null是怎么做的,看结构怎么样
解释(个人理解,没有严格验证过):
Rust 里的函数指针和引用一样是非空类型,所以在 release 下会假设为非空值来优化,与 0 比较时返回
false
。楼主使用的应该是 64 位机器,所以将函数指针转换为小于 64 位宽度的整数时需要截断,这种情况就不能简单地做非空值优化,所以在 release 下转换为
u8
u16
u32
进行了实际转换和比较,得到了正确的true
结果。解决方案:C 函数声明里返回类型改为
Option<CCallbackType>
:这时判断空可以直接用
output.is_none()
,还可以用模式匹配。跟 FFI 没有关系,rust 在 release 的情况下直接对
output as i64 == 0
做了优化,认为output as i64
是函数指针,一定不为 0,所以直接在编译时优化output as i64 == 0
为 false。一个最小可复现例子:https://godbolt.org/z/sYsTvf79f
汇编:https://imgtu.com/i/hpF69x
至于 i8 和 i32 为啥没被优化我就不知道了