首先,您的ToPtr
实现可能会导致不安全的代码。如下所示:
impl ToPtr for str {
fn to_ptr(&self) -> *const i8 {
CString::new(self).unwrap().as_ptr()
}
}
这将分配一个新的 CString
并返回其内容的指针,但是当 to_ptr
返回时,CString
将被删除,因此这是一个悬空指针。 任何对此指针的引用都是未定义的行为。 文档 对此有一个重要的警告,但这仍然是一个非常常见的错误。
从字符串字面量创建 *const c_char
的一种正确方法是 b"string here\0".as_ptr() as *const c_char
。该字符串以 null 结尾,并且没有悬空指针,因为字符串字面量在整个程序中都存在。如果您要转换非常量字符串,则在使用它时必须使 CString
保持活动状态,如下所示:
let s = "foo";
let cs = CString::new(s).unwrap();
unsafe { some_c_function(cs.as_ptr()); }
补充说明:一个编辑人员"建议"(我是 Stack Overflow 的新手,但似乎评论比重写我的答案更礼貌)上面的代码可以这样写:
let s = "foo";
unsafe {
some_c_function(CString::new(s).unwrap().as_ptr());
}
尽管这是技术上正确的(最好的一种正确),但所涉及到的"临时降低规则"很微妙——这是因为
as_ptr
接受一个对
CString
(实际上是&CStr,因为编译器将方法链更改为CString::new(s).unwrap().deref().as_ptr())的引用而不是消耗它,并且因为我们只有一个要调用的C函数。在写不安全代码时,我不喜欢依赖于任何微妙或不明显的东西。
除此之外,我
修复了你代码中的不安全性(你所有的调用都使用字符串字面量,所以我只是使用了我上面提到的第一种策略)。在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
这似乎证实了Rust的结果。在
/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。那么问题出在哪里呢?