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

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

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

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

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

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

  1. extern crate alloc;
  2. use std::{
  3. ffi::{c_char, c_int, c_uint},
  4. ptr::null_mut,
  5. };
  6. pub enum HWND__ {}
  7. pub type HWND = *mut HWND__;
  8. pub type DWORD = u32;
  9. pub type LPCCH = *const c_char;
  10. pub type LPCWSTR = *const u16;
  11. pub type LPWSTR = *mut u16;
  12. pub type UINT = c_uint;
  13. pub const CP_UTF8: DWORD = 65001;
  14. pub const MB_ERR_INVALID_CHARS: DWORD = 0x08;
  15. pub const MB_OK: UINT = 0x00000000;
  16. macro_rules! wide_str {
  17. ($($arg:tt)*) => {{
  18. let utf8 = alloc::fmt::format(format_args!($($arg)*));
  19. const BUFFER_LEN: usize = 256;
  20. let utf16: &[u16; BUFFER_LEN] = {
  21. let mut utf16 = [0; BUFFER_LEN];
  22. let result = MultiByteToWideChar(
  23. CP_UTF8,
  24. MB_ERR_INVALID_CHARS,
  25. utf8.as_ptr() as LPCCH,
  26. utf8.len() as core::ffi::c_int,
  27. utf16.as_mut_ptr() as LPWSTR,
  28. utf16.len() as core::ffi::c_int,
  29. );
  30. assert!(result != 0, "error converting utf8 to utf16");
  31. &{ utf16 }
  32. };
  33. utf16.as_ptr()
  34. }};
  35. }
  36. #[link(name = "kernel32")]
  37. extern "system" {
  38. pub fn MultiByteToWideChar(
  39. CodePage: UINT,
  40. dwFlags: DWORD,
  41. lpMultiByteStr: LPCCH,
  42. cbMultiByte: c_int,
  43. lpWideCharStr: LPWSTR,
  44. cchWideChar: c_int,
  45. ) -> c_int;
  46. }
  47. #[link(name = "user32")]
  48. extern "system" {
  49. pub fn MessageBoxW(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int;
  50. }
  51. fn main() {
  52. unsafe {
  53. MessageBoxW(
  54. null_mut(),
  55. wide_str!("Message!"),
  56. wide_str!("Caption!"),
  57. MB_OK,
  58. );
  59. }
  60. }

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

okxuctiv

okxuctiv1#

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

  1. macro_rules! wide_str {
  2. ($($arg:tt)*) => {{
  3. let utf8 = alloc::fmt::format(format_args!($($arg)*));
  4. const BUFFER_LEN: usize = 256;
  5. let mut utf16 = [0; BUFFER_LEN];
  6. let result = MultiByteToWideChar(
  7. CP_UTF8,
  8. MB_ERR_INVALID_CHARS,
  9. utf8.as_ptr() as LPCCH,
  10. utf8.len() as core::ffi::c_int,
  11. utf16.as_mut_ptr() as LPWSTR,
  12. utf16.len() as core::ffi::c_int,
  13. );
  14. assert!(result != 0, "error converting utf8 to utf16");
  15. utf16
  16. }};
  17. }
  18. fn main() {
  19. unsafe {
  20. MessageBoxW(
  21. null_mut(),
  22. wide_str!("Message!").as_ptr(),
  23. wide_str!("Caption!").as_ptr(),
  24. MB_OK,
  25. );
  26. }
  27. }

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

展开查看全部

相关问题