You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何未对齐指针的解引用操作在Rust中可正常运行?

未对齐指针解引用为何未触发断言?

在Rust中,将*const u8转换为*const T是合法的,但解引用转换后的指针属于unsafe操作——因为指向的内存可能不满足T对大小、对齐(alignment)及有效字节模式的要求。我尝试构造一个仅违反对齐要求、但满足另外两个条件的示例:生成一个7个u8的切片,将不同起始位置的长度为4的子切片解释为f32值。由于任意字节模式对f32均有效,且4个u8的大小正好等于size_of::<f32>(),因此唯一变量是子切片指针的对齐度。

运行代码

use std::mem::transmute;
use std::ptr::read;
use std::convert::TryInto;
//use rand::Rng;

fn to_f32(v: &[u8]) -> f32 {
    let ptr = v.as_ptr() as *const f32;
    unsafe {
        // [1] 直接解引用
        *ptr
        // [2] 另一种方式
        //ptr.read()
    }
}

fn main() {
    println!("align_of::<f32>() = {}", std::mem::align_of::<f32>());

    //let mut rng = rand::thread_rng();

    // 栈上的数组
    let v: [u8; 7] = [0x4A, 0x3A, 0x2a, 0x10, 0x0F, 0xD2, 0x37];
    // 堆上的数组
    //let v = Box::new(rng.gen::<[u8;7]>());

    for i in 0..4 {
        let ptr = &v[i..(i+4)];
        let f = to_f32(ptr);

        // 计算指针的最大对齐度
        let alignment = 1 << (ptr.as_ptr() as usize).trailing_zeros();
        
        // 作为对照的其他转换方式
        let repr = ptr.try_into().expect("");
        let f2 = unsafe { transmute::<[u8; 4], f32>(repr) };
        let f3 = f32::from_le_bytes(repr);

        println!("{:x?} [{alignment}]: {repr:02x?} : {f} =? {f2} = {f3}", ptr.as_ptr());
    
        assert_eq!(f, f2);
        assert_eq!(f, f3);
    }
}

代码输出

align_of::<f32>() = 4
0x7fffa431a5d1 [1]: [4a, 3a, 2a, 10] : 0.000000000000000000000000000033571493 =? 0.000000000000000000000000000033571493 = 0.000000000000000000000000000033571493
0x7fffa431a5d2 [2]: [3a, 2a, 10, 0f] : 0.000000000000000000000000000007107881 =? 0.000000000000000000000000000007107881 = 0.000000000000000000000000000007107881
0x7fffa431a5d3 [1]: [2a, 10, 0f, d2] : -153612880000 =? -153612880000 = -153612880000
0x7fffa431a5d4 [4]: [10, 0f, d2, 37] : 0.000025040965 =? 0.000025040965 = 0.000025040965

问题

为何这段代码从未触发断言,即便它[1]不安全地解引用了未对齐指针,或是[2]调用了明确要求有效对齐的ptr::read()?


解答

核心原因是未对齐访问属于未定义行为(Undefined Behavior, UB),而未定义行为的特点就是没有任何保证:它可能“看起来正常工作”,也可能在任意场景下突然崩溃、输出错误结果,甚至引发更诡异的问题。具体到你的场景:

  • 硬件架构特性:你大概率是在x86/x86_64架构的CPU上运行代码——这类架构原生支持大部分未对齐内存访问(只是会有性能损耗),所以未对齐读取恰好能拿到正确的字节序列,断言自然不会触发。但如果换成ARM、RISC-V等严格要求对齐的架构,未对齐访问会直接触发总线错误,程序瞬间崩溃。
  • Rust的unsafe契约:ptr::read()确实要求指针满足对齐要求,但违反这个要求属于UB,编译器不会为UB做任何检查或兜底——它只会假设你已经保证了前置条件成立,不会主动触发断言或报错。
  • 断言的巧合性:你对比的是未对齐读取结果和合规转换的结果,在x86架构下,未对齐读取恰好能正确读取连续的4字节数据,所以两者相等,但这完全是架构特性带来的巧合,不代表代码合法。

简单来说:你看到的“正常运行”只是未定义行为在当前环境下的一种表现,绝不能认为这段代码是安全的。只要换个环境、开启更高等级的编译器优化,程序随时可能出问题。

内容的提问来源于stack exchange,提问作者GoodArtistsCopy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:51:30