2016-08-26 10 views
3

Для образовательных целей я пытаюсь получить доступ к FILE структуры в Руст:Неправильное отображение C структуры в Rust

unsafe { 
    let passwd = libc::fopen("/etc/passwd".to_ptr(), &('r' as libc::c_char)); 
    let fp = &mut *(passwd as *mut MY_FILE); 
    println!("flags={}, file={}", fp._flags, fp._file); 
} 

MY_FILE структура я получил, запустив bindgen на stdio.h (я на OS X) :

bindgen /usr/include/stdio.h 

Как-то _flags всегда 8 для файлов, открытых в режиме записи (4 в режиме чтения), так что это флаги, кажется, от (я тестировал с C кодом, чтобы убедиться, что он действительно не 4 или 8). Указатель файла, однако, кажется правильным. Что может быть причиной этого? Я извлекаю привязку из неправильного файла заголовка? Есть ли что-то, что мне нужно добавить в атрибут #[repr(C,)]?

Here - полный код, включая структуру.

Это прослеживание вопрос от an earlier question

+0

Код, который вы отправили, не скомпилирован (путаница fp и passwd, я думаю). Кроме того, строки, которые вы передаете функции C, не завершены NUL, поэтому ваш код имеет полностью неопределенное поведение. –

+0

О, да, я забыл добавить один файл, который завершает NUL, я обновил gist выше – hansaplast

+0

Не беспокоился об утечке строки каждый раз? Во всяком случае, я думаю, что второй аргумент имеет ту же проблему. –

ответ

2

Во-первых, ваша реализация ToPtr приглашает необоснованное кода. Воспроизводятся здесь:

// code in italics is wrong 
impl ToPtr for str { 
    fn to_ptr(&self) -> *const i8 { 
     CString::new(self).unwrap().as_ptr() 
    } 
} 

Это выделяет новый CString и возвращает указатель на его содержание, но CString отбрасывается, когда to_ptr возвращается, так что это оборванный указатель. Любое разыменование этого указателя является неопределенным поведением.documentation имеет большое предупреждение об этом, но это по-прежнему очень распространенная ошибка.

Один правильный способ сделать *const c_char из строкового литерала - b"string here\0".as_ptr() as *const c_char. Строка имеет нулевое завершение, и нет никакого висячего указателя, потому что строковые литералы живут для всей программы. Если у вас есть непостоянная строка для преобразования, вы должны держать CString живы, пока он используется, например:

let s = "foo"; 
let cs = CString::new(s).unwrap(); // don't call .as_ptr(), so the CString stays alive 
unsafe { some_c_function(cs.as_ptr()); } 
// CString is dropped here, after we're done with it 

Помимо: редактор «предложил» (я новый в Stack Overflow, но мне кажется, более вежлив комментировать, а не пытаться переписать мой ответ), что приведенный выше код может быть записан следующим образом:

let s = "foo"; 
unsafe { 
    // due to temporary drop rules, the CString will be dropped at the end of the statement (the `;`) 
    some_c_function(CString::new(s).unwrap().as_ptr()); 
} 

Хотя это технически правильно (лучший вид правильно), «временные правила падения» - тонкие - это работает, потому что as_ptr принимает ссылку на CString (на самом деле & CStr, потому что компилятор изменяет метод цепочки в CString :: новый (s) .unwrap() deref() as_ptr().). вместо того, чтобы потреблять его, и потому что у нас есть только одна функция C для вызова. Я не люблю полагаться на что-то тонкое или неочевидное при написании небезопасного кода.


С этим из пути, я fixed that unsoundness в коде (вызовы все использовать строковые литералы, так что я просто использовал свою первую стратегию выше).Я получаю этот вывод по OSX:

0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0 
0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0 
0, 0, 8, 2, 0, 0, 0, 0, 0, 0, 0 
0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0 

Итак, это соответствует вашим результатам, не так ли? Я также написал следующую программу C:

#include <stdio.h> 
#include <unistd.h> 

int main() { 
    struct __sFILE *fp1 = fdopen(STDIN_FILENO, "r"); 
    struct __sFILE *fp2 = fdopen(STDOUT_FILENO, "w"); 
    struct __sFILE *fp3 = fdopen(STDERR_FILENO, "w"); 
    struct __sFILE *passwd = fopen("/etc/passwd", "r"); 

    printf("%i %i %i %i\n", fp1->_flags, fp2->_flags, fp3->_flags, passwd->_flags); 
} 

И получил результат:

4 8 8 4 

который, кажется, оправдает результаты Руст. Существует комментарий в верхней части /usr/include/stdio.h, который говорит:

/* 
* The following always hold: 
* 
* if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR), 
*  _lbfsize is -_bf._size, else _lbfsize is 0 
* if _flags&__SRD, _w is 0 
* if _flags&__SWR, _r is 0 
*/ 

И глядя эти константы:

#define __SLBF 0x0001  /* line buffered */ 
#define __SRD 0x0004  /* OK to read */ 
#define __SWR 0x0008  /* OK to write */ 

Это, кажется, соответствует на выходе мы получаем: 4 для файла, открытого в режиме чтения, 8 для записи. Так в чем проблема?

+0

Спасибо за объяснение об оборванных указателях, эти вещи очень важны. Это в документе, и я не читал. Затем: код C, который у меня был, был из передовой книги по программированию Unix. После того, как я узнал, что большинство структурных переменных FILE заполняются только при чтении/записи потока, я нашел это предложение в книге: «Обратите внимание, что мы выполняем ввод-вывод в каждом потоке перед печатью состояния буферизации, поскольку первый ввод/Вывода обычно вызывает выделение буферов для потока »(опять же RTFM мне ..). Рабочий принцип: https://gist.github.com/philippkeller/c683dda9d89ca0f8cb6830b6dc52910b – hansaplast

 Смежные вопросы

  • Нет связанных вопросов^_^