CString::new().unwrap().as_ptr() дает пустой *const c_char

У меня есть функция C, которая ожидает *const std::os::raw::c_char, и я сделал следующее в Rust:

use std::os::raw::c_char;
use std::ffi::{CString, CStr};
extern crate libc;

fn main() {
    let _test_str: *const c_char = CString::new("Hello World").unwrap().as_ptr();
    let fmt: *const c_char = CString::new("%s\n").unwrap().as_ptr();
    unsafe { libc::printf(fmt, _test_str); }

    unsafe {
        let slice = CStr::from_ptr(_test_str);
        println!("string buffer size without nul terminator: {}", slice.to_bytes().len());
    }
}

Однако я не могу распечатать _test_str, и вывод вышеуказанной программы просто

string buffer size without nul terminator: 0

Если я передам _test_str в какую-нибудь функцию C и увижу, что это пустая строка. Что я сделал не так?


person xxks-kkk    schedule 04.09.2018    source источник
comment
Возможный дубликат преобразования между Rust str и ffi::CString и обратно частично искажает строку   -  person trentcl    schedule 05.09.2018
comment
@trentcl Не ожидал увидеть предупреждение, если я буду продолжать смотреть на вводную часть страницы вместо объяснения подраздела функции as_ptr. Мой плохой, но безрассудный слишком много. В любом случае спасибо за указание на ссылку. Кроме того, вопрос, на который вы указали, гораздо более длинный и сложный для понимания ключевого момента.   -  person xxks-kkk    schedule 05.09.2018
comment
Прошу прощения за то, что не дал прямой ссылки на соответствующий раздел, но мне не нужно было вообще ссылаться на него; вы несете ответственность за обеспечение любого unsafe кода, который вы пишете, включая чтение документации по функциям, которые возвращают необработанные указатели, прежде чем разыменовывать их. Что касается обмана, я согласен, что конкретный немного многословен. На самом деле Как остановить утечку памяти при использовании as_ptr()? наверное лучше. Тем не менее, я получил только один близкий голос, поэтому я не могу изменить его сейчас.   -  person trentcl    schedule 05.09.2018


Ответы (1)


Вы создаете CString в том же операторе, что и указатель на него. CString принадлежит, но не привязан к переменной, поэтому он существует только до тех пор, пока закрывающий оператор, в результате чего указатель становится недействительным. Об этом специально предупреждает документация для as_ptr:

Например, следующий код вызовет неопределенное поведение при использовании ptr внутри небезопасного блока:

use std::ffi::{CString};

let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
unsafe {
    // `ptr` is dangling
    *ptr;
}

Это происходит потому, что указатель, возвращаемый as_ptr, не несет никакой информации о времени жизни, а CString освобождается сразу после вычисления выражения CString::new("Hello").expect("CString::new failed").as_ptr().

Вы можете решить эту проблему, введя переменные, которые будут жить для всей функции, а затем создайте указатели на эти переменные:

fn main() {
    let owned_test = CString::new("Hello World").unwrap();
    let _test_str: *const c_char = owned_test.as_ptr();
    let owned_fmt = CString::new("%s\n").unwrap();
    let fmt: *const c_char = owned_fmt.as_ptr();

    unsafe {
        libc::printf(fmt, _test_str);
    }

    unsafe {
        let slice = CStr::from_ptr(_test_str);
        println!(
            "string buffer size without nul terminator: {}",
            slice.to_bytes().len()
        );
    }

    // owned_fmt is dropped here, making fmt invalid
    // owned_test is dropped here, making _test_str invalid
}

Если вы работаете с необработанными указателями, вам нужно быть особенно осторожным, чтобы они всегда указывали на оперативные данные. Введение переменной — лучший способ точно контролировать, как долго живут эти данные — они будут жить с момента инициализации переменной до того момента, когда переменная выйдет из области видимости.

person Peter Hall    schedule 04.09.2018