为什么我的Rust应用程序对于每个不同的优化级别都有不同的行为?

xxls0lw8  于 2023-03-30  发布在  其他
关注(0)|答案(1)|浏览(140)

我有一个Rust程序,它的行为取决于应用的优化级别。这是一个简单的程序,显示Win32 MessageBoxW
以下是opt-level = 0的输出:

下面是opt-level = 1的输出:

下面是opt-level = 2的输出:

下面是程序的一个最小可重复的例子。它不太一样,因为我的二进制是no_std(因此我使用alloc crate),但是每个优化级别的行为都是相似的。

extern crate alloc;

use std::{
    ffi::{c_char, c_int, c_uint},
    ptr::null_mut,
};

pub enum HWND__ {}
pub type HWND = *mut HWND__;

pub type DWORD = u32;
pub type LPCCH = *const c_char;
pub type LPCWSTR = *const u16;
pub type LPWSTR = *mut u16;
pub type UINT = c_uint;

pub const CP_UTF8: DWORD = 65001;
pub const MB_ERR_INVALID_CHARS: DWORD = 0x08;
pub const MB_OK: UINT = 0x00000000;

macro_rules! wide_str {
    ($($arg:tt)*) => {{
        let utf8 = alloc::fmt::format(format_args!($($arg)*));
        const BUFFER_LEN: usize = 256;
        let utf16: &[u16; BUFFER_LEN] = {
            let mut utf16 = [0; BUFFER_LEN];

            let result = MultiByteToWideChar(
                CP_UTF8,
                MB_ERR_INVALID_CHARS,
                utf8.as_ptr() as LPCCH,
                utf8.len() as core::ffi::c_int,
                utf16.as_mut_ptr() as LPWSTR,
                utf16.len() as core::ffi::c_int,
            );
            assert!(result != 0, "error converting utf8 to utf16");

            &{ utf16 }
        };
        utf16.as_ptr()
    }};
}

#[link(name = "kernel32")]
extern "system" {
    pub fn MultiByteToWideChar(
        CodePage: UINT,
        dwFlags: DWORD,
        lpMultiByteStr: LPCCH,
        cbMultiByte: c_int,
        lpWideCharStr: LPWSTR,
        cchWideChar: c_int,
    ) -> c_int;
}

#[link(name = "user32")]
extern "system" {
    pub fn MessageBoxW(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int;
}

fn main() {
    unsafe {
        MessageBoxW(
            null_mut(),
            wide_str!("Message!"),
            wide_str!("Caption!"),
            MB_OK,
        );
    }
}

为什么会出现这种产量差异,如何解决?

okxuctiv

okxuctiv1#

wide_str!创建的指针是悬空的,因为utf16(具有相同名称的内部和外部变量)在MessageBoxW被调用之前就被破坏了。它可能会中断并导致像这样的不一致,甚至更糟的是,它可能看起来有效,但后来或以更微妙的方式被中断。
为了避免将来的误用,我建议从你的宏中返回一个拥有数据的值,你可以手动调用.as_ptr()。类似这样:

macro_rules! wide_str {
    ($($arg:tt)*) => {{
        let utf8 = alloc::fmt::format(format_args!($($arg)*));
        const BUFFER_LEN: usize = 256;
        let mut utf16 = [0; BUFFER_LEN];

        let result = MultiByteToWideChar(
            CP_UTF8,
            MB_ERR_INVALID_CHARS,
            utf8.as_ptr() as LPCCH,
            utf8.len() as core::ffi::c_int,
            utf16.as_mut_ptr() as LPWSTR,
            utf16.len() as core::ffi::c_int,
        );
        assert!(result != 0, "error converting utf8 to utf16");

        utf16
    }};
}

fn main() {
    unsafe {
        MessageBoxW(
            null_mut(),
            wide_str!("Message!").as_ptr(),
            wide_str!("Caption!").as_ptr(),
            MB_OK,
        );
    }
}

这并没有表现出你所看到的行为。

相关问题