2017-07-12 10 views
1

私はRustにC関数をラップしようとしています。以下の構造体を返しstruct elem* get_list() C機能:錆でRustから読み取ったときにC構造体が整列していない迷惑データを返すのはなぜですか?

struct elem { 
    char data[5], 
    struct elem* next 
}; 

は、私は、関数に次のように宣言しました。 C関数の宣言は、古いバージョンのRustドキュメントに記述されているように*const c_voidを返します。これは書面の時点では見つかりませんでした。

extern "C" { 
    pub fn get_list() -> *const c_void; 
} 

構造体がnextは、リストの次の要素へのポインタであることと、リンクリストを表します。私は*const elemを返すとポインタと協力して、同じ結果を達成することを試みました。錆の内部では、私は次のように構造体を宣言した:

#[repr(C)] 
pub struct elem { 
    pub data: [u8; 5], 
    pub next: *const c_void, 
} 

関数は(タイプelemの)リンクリストの最初の要素を*const c_voidポインタを返します。これは、ジャンクデータを読み込む

let head = get_list(); 
while !head.is_null() { 
    let el: &elem = mem::transmute(head); 
    let str = el.data; 
    let str = CStr::from_bytes_with_nul(&str).unwrap(); 
    //do something 
    head = el.next(); 
} 

- ポインタが適切に整列されていない、両方の文字列が悪く、非nullで終わる、と:私は次のコードとリンクリストの要素を読み込むしようとしています次のポインタはランダムなデータにつながります(関数がCから直接呼び出されると、リストのサイズが異なります)。

私は機能がelemへのポインタを返すと、ポインタのみで作業をしてみました、私はelのアドレスからstrを核変換してみました - それは、常に同じジャンクデータを読み込みます。正しく整列させるにはどうすればいいですか?

私は配列の代わりにポインタを使ってそれを行う方法を知っています。これはRustのドキュメントで説明されていますが、Cコードを変更することはできません。

+0

には注意してくださいどのようにget_list' 'のCの実装が宣言されていますか? (私は '* const elem'の代わりに' * const c_void'を返す理由について興味があります) – pnkfelix

+0

[これは動作しています](https://play.rust-lang.org/?gist=5f56804e93f608e811727af85d376f52&version=stable ) – red75prime

+0

また、反復のどの時点で、ミスアライメントされたデータが読み込まれているのを見ていますか?最初の 'head = get_list()'の位置がずれていますか?それとも、最初の反復の後のいつかですか? – pnkfelix

答えて

1

私はこのケースのためのサンプルライブラリを書いた後、それはexternの問題ではなく、むしろCStrというものでした。私が最初のNULターミネーターの位置にバッファーをスライスする例では固定されているので、私は適切なエクスターナルのために書いた例を提供します。

list.c

#include <stdlib.h> 
#include <string.h> 

struct elem { 
    char data[5]; 
    struct elem* next; 
}; 

struct elem* get_list() { 
    struct elem* head = malloc(sizeof(struct elem)); 
    strcpy(head->data, "1"); 

    struct elem* el = malloc(sizeof(struct elem)); 
    head->next = el; 

    strcpy(el->data, "2"); 

    el->next = malloc(sizeof(struct elem)); 
    el = el->next; 
    strcpy(el->data, "3"); 
    el->next = NULL; 

    return head; 
} 

main.rs

use std::ffi::CStr; 

#[repr(C)] 
pub struct elem { 
    pub data: [u8; 5], 
    pub next: *const elem 
} 

#[link(name = "list", kind = "static")] 
extern { 
    pub fn get_list() -> *const elem; 
} 

fn main() { 
    unsafe { 
     let mut list = get_list(); 
     // Note, that, if we call from_bytes_with_nul it will throw 
     // an NulInternal error, therefore, 
     // we have to slice the buffer to the first NUL-terminator 
     while !list.is_null() { 
      let mut null_pos = (*list).data.len() - 1; 
      { 
       for i in 0..(*list).data.len() { 
        if (*list).data[i] == 0 { 
         null_pos = i + 1; 
         break 
        } 
       } 
      } 
      let str = CStr::from_bytes_with_nul(
          (*list).data[..null_pos] 
        ).unwrap(); 
      println!("{:?}", str); 
      list = (*list).next; 
     } 
    } 
} 

出力

"1" 
"2" 
"3" 
実装の

重要な側面:

  • #[repr(C)]と注釈された同じ構造を定義すると、Cと同じ方法で整列された になります。

  • constポインタを構造体に返すextern関数を定義します。代わりにstd::mem::transmute

  • 使用ポインタがnullポインタとターミネータ

+0

それは*人々が[MCVE]を提供してほしい理由です。 – Shepmaster

関連する問題