在Swift中将“ char path [MAX]”的地址传递给“ const char *”的参数的最佳方法

时间:2019-06-14 18:38:15

标签: c swift

我意识到那里有“如何将char[] / char*转换为Swift Sting”问题的上百万种形式,以及它们的反形式,所有这些都已被提出并回答。

我不是问这个。

在Swift中,我要做的就是简单地将C char数组的地址(通过C函数获得)传递给另一个C函数的C char*指针参数。 / p>

具体来说,我正在尝试复制以下C代码,其中将char字段中包含的stat.f_mntonname数组的地址作为getattrlist(const char*, ...)调用的第一个参数传递:

// Get volume stat
const char* path = ...;
struct statfs volStat;
if (statfs(path,&volStat)==-1) { error }

// statfs has the mount point of the volume; use that to get attributes
struct attrlist request;
// ... set up request here
struct volAttrs {
    // ... response values
}

if (getattrlist(volStat.f_mntonname,&request,&volAttrs,sizeof(volAttrs),FSOPT_NOFOLLOW)==-1) { error }

问题似乎是Swift将stat.f_mntonname字段而不是数组解释为包含MAXPATHLEN个数量的Int8值的元组;换句话说,(Int8,Int8,Int8,Int8,Int8,...,Int8)

在互联网上闲逛很多次之后,我终于找到了解决方法:

var volStat = statfs()
guard statfs(fileURL.path, &volStat) != -1 else {
    ErrorExit(cause: "statfs")
}
var attrRequest = attrlist()
// set up getattrlist request ...
var attrs = XtraVolumeAttrs()
guard getattrlist(UnsafeRawPointer(&volStat.f_mntonname.0).bindMemory(to: CChar.self, capacity: Int(MAXPATHLEN)),
                  &attrRequest,
                  &attrs,
                  MemoryLayout<XtraVolumeAttrs>.size,
                  UInt32(FSOPT_NOFOLLOW)) != -1 else {
    ErrorExit(cause: "getattrlist")
}

所以魔术UnsafeRawPointer(&volStat.f_mntonname.0).bindMemory(to: CChar.self, capacity: Int(MAXPATHLEN)似乎完成了将char[MAXPATHLEN]数组转换为char*的任务,但是男孩是丑陋,不直观的,并且-如果我说实话—我什至不确定这是正确的(除了代码有效的事实之外)。

我觉得必须有一个更好的方法,我希望有人能发布它。

1 个答案:

答案 0 :(得分:1)

这很丑陋,因为Swift将C数组作为元组导入,并且不会自动转换为数组或指针。

正如Hamish所指出的那样,UnsafeRawPointer(&volStat.f_mntonname.0)的使用是不正确的,因为从初始值设定项返回时,创建的指针可能无效。

一个安全的版本是

let retval = withUnsafeBytes(of: volStat.f_mntonname) { 
    getattrlist($0.bindMemory(to: Int8.self).baseAddress, /* other args */)
}

在覆盖元组原始字节的“缓冲区指针”上调用bindMemory(),因此我们不必显式指定容量。