在Swift中转换为不同的C struct unsafe指针

时间:2016-08-27 18:41:32

标签: swift swift3 unsafe-pointers

我想从Swift调用Posix套接字函数socketbindsocket非常简单 - 需要Int32,但bind会导致问题,因为我有一个sockaddr_in指针,但它需要一个sockaddr指针。在C中,这将是一个演员,如:

bind(sock, (struct sockaddr *)&sockAddress, sizeof(sockAddress))

这是Swift的尝试:

let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()        
bind(sock, &sockAddress, UInt32(MemoryLayout<sockaddr_in>.size))

bind行无法编译:无法转换类型&#39; sockaddr_in&#39;的值预期参数类型&#39; sockaddr&#39;

如何投射指针?

3 个答案:

答案 0 :(得分:7)

你可以这样写:

withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
    sockaddrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) {sockaddrPtr in
        bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
    }
}

或有人建议这可能会更好:

withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
    let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
    bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}

This article可能会有所帮助。

(UPDATE) 如the link shown by Martin R中所述,现在MemoryLayout<T>.strideMemoryLayout<T>.size返回的值与C sizeof一致,其中T是导入的C结构。我会在这里保留stride版本的答案,但这不是&#34;必需&#34;在这种情况下现在。

答案 1 :(得分:4)

在Swift 3中你必须&#34;重新绑定&#34;指针 (比较SE-0107 UnsafeRawPointer API):

let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()

let result = withUnsafePointer(to: &sockAddress) {
    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
        bind(sock, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
    }
}

说明:

  • 不需要let sock: Int32var sockAddress: sockaddr_in中的类型注释。

  • memset()不是必需的,因为sockaddr_in()初始化 所有结构成员都归零。

  • C sizeof的Swift等价物为stride(包括 一个可能的结构填充),而不是size(不包括 struct padding)。(这&#34;问题&#34;不再存在。 对于从C导入的结构,stridesize具有相同的值。)

答案 2 :(得分:0)

Swift 5淘汰了withUnsafeBytes(UnsafePointer<sockaddr>),所以下面是我对Swift 5所做的事情:

        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    return withUnsafeBytes { (p: UnsafeRawBufferPointer) -> String? in
        let addr = p.baseAddress?.assumingMemoryBound(to: sockaddr.self)
        guard getnameinfo(addr, socklen_t(self.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
            return nil
        }
        return String(cString: hostname)
    }