对于共享引用和可变引用,语义很明确:as
所以这个代码:
#[no_mangle]
pub extern fn run_ref(a: &i32, b: &mut i32) -> (i32, i32) {
let x = *a;
*b = 1;
let y = *a;
(x, y)
}
编译(在x86_64上)为:
run_ref:
movl (%rdi), %ecx
movl $1, (%rsi)
movq %rcx, %rax
shlq $32, %rax
orq %rcx, %rax
retq
请注意,a
指向的内存只读取一次,因为
原始指针更复杂.原始指针算法和强制转换是
我们可以将原始指针转换回共享和可变的引用
但是如果我们直接使用原始指针,语义是什么呢?
#[no_mangle]
pub unsafe extern fn run_ptr_direct(a: *const i32, b: *mut f32) -> (i32, i32) {
let x = *a;
*b = 1.0;
let y = *a;
(x, y)
}
汇编至:
run_ptr_direct:
movl (%rdi), %ecx
movl $1065353216, (%rsi)
movl (%rdi), %eax
shlq $32, %rax
orq %rcx, %rax
retq
虽然我们写了一个不同类型的值,但第二次读取仍然进行
请注意,一个普通的优化C/C++编译器将消除第二个问题
struct tuple { int x; int y; };
extern "C" tuple run_ptr(int const* a, float* b) {
int const x = *a;
*b = 1.0;
int const y = *a;
return tuple{x, y};
}
汇编至:
run_ptr:
movl (%rdi), %eax
movl $0x3f800000, (%rsi)
movq %rax, %rdx
salq $32, %rdx
orq %rdx, %rax
ret
Playground with Rust code examples
godbolt Compiler Explorer with C example
那么:如果我们直接使用原始指针,语义是什么呢
这对编译器是否被允许有直接影响