2017-03-10 2 views
0

나는 Rust에서 libc 라이브러리로 직접 연결되는 셸을 작성하려고 시도 해왔다. Vec<String>을 사용하여 인수를 execvp()으로 전달했지만 char **으로 변환하지 못한 것 같습니다. 실행시 모든 매개 변수가 널 문자열이되었습니다.Rust에서 Vec <String>을 C로 변환 **

다음은 관련된 코드입니다.

fn safe_execvp(path: String, argv: Vec<String>) -> Result<(), i32> { 
    unsafe { 
     let c_path = CString::new(path.as_str()).unwrap(); 
     let mut c_argv_vec = Vec::new(); 
     for arg in &argv { 
      let c_arg = CString::new(arg.as_str()).unwrap().as_ptr(); 
      c_argv_vec.push(c_arg); 
     } 
     c_argv_vec.push(std::ptr::null()); 
     match execvp(c_file.as_ptr(), c_argv_vec.as_ptr()) { 
      num => Err(num), 
     } 
    } 
} 

execvpfn execvp(file: *const i8, argv: *const*const i8) -> i32;는로 정의 C 라이브러리 함수이다.

내가 뭘 잘못했는지 잘 모르겠다. 인수에 대한 기억이 execvp()에 전화하기 전에 해제 되었기 때문입니까?

답변

5

CString 인스턴스를 만들고 즉시 포인터를 가져 오는 중입니다. 결과적으로, 잘 추측했듯이이 문자열의 소유권은 조기에 삭제되었습니다. 포인터가 평생 정보를 보유하지 않기 때문에이 경우에는 컴파일 오류가 발생하지 않는다는 점을 제외하고는 로컬 인스턴스에 대한 참조를 반환하는 것과 비슷합니다.

문제의 해결책은 함수의 범위에서 소유 된 C 스타일 문자열을 유지하고 동일한 내용의 포인터에 대한 포인터를 별도로 생성하는 것입니다.

let cstr_argv: Vec<_> = argv.iter() 
     .map(|arg| CString::new(arg.as_str()).unwrap()) 
     .collect(); 

let mut p_argv: Vec<_> = cstr_argv.iter() // do NOT into_iter() 
     .map(|arg| arg.as_ptr()) 
     .collect(); 

p_argv.push(std::ptr::null()); 

let p: *const *const c_char = p_argv.as_ptr(); 

Playground.

+0

고마워요! 나는'CString'을 벡터 안에 두어야한다고 생각합니다. –

4

내가 제안 당신의 CString::as_ptragain에 대한 설명서를 읽어

경고

기본 메모리가 너무 일찍 해제되지 있는지 확인하는 것은 귀하의 책임입니다. PTR은 안전하지 않은 블록 내에서 사용하는 경우 예를 들어, 다음 코드는 정의되지 않은 동작이 발생할 수 :

use std::ffi::{CString}; 

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

을 당신은 설명서를 할 수 없습니다라고 정확히하고 있습니다.

+0

감사합니다. 나는 그 부분을 읽었지만 내가 경고하지 않은 실수를 저도 깨닫지 못했습니다. –