【问题标题】:Cast to different C struct unsafe pointer in Swift在 Swift 中转换为不同的 C 结构不安全指针
【发布时间】:2017-01-04 04:53:02
【问题描述】:

我想从 Swift 调用 Posix 套接字函数 socketbindsocket 非常简单——它需要Int32s,但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 行编译失败:无法将“sockaddr_in”类型的值转换为预期的参数类型“sockaddr”

如何转换指针?

【问题讨论】:

    标签: swift swift3 unsafe-pointers


    【解决方案1】:

    你可以这样写:

    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 可能会有所帮助。


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

    【讨论】:

    • 哎呀,没有看到你的答案:)
    • This article 建议 UnsafeRawPointer 在某些情况下有效。但我不确定在这种情况下哪个更好。欢迎任何进一步的信息。
    • 根据lists.swift.org/pipermail/swift-users/Week-of-Mon-20160815/…的说法,两者都是正确的,据我了解,稍微偏爱“withMemoryRebound”。
    • @MartinR,谢谢。我无法确定,稍微看了一眼这篇文章。我会花更多时间再读一遍。
    • 这里stackoverflow.com/a/33177600/1187415 是“大小”不起作用的情况,只有“步幅”。它低级...
    【解决方案2】:

    在 Swift 3 中,您必须“重新绑定”指针 (比较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)。(这个“问题”不再存在。 对于从 C 导入的结构,stridesize 具有相同的值。)

    【讨论】:

    • 如果您尝试使用sizeof,Xcode 建议将其替换为size
    • @RobN: 然后为 Xcode 提交错误报告 :) 在 C 中,sizeof 是结构的大小,包括末尾可能的填充。在 Swift 中,stride 包括结构填充,而 size 不包括。
    • 哈。 :) 好吧,我会让其他人这样做,因为我不知道这是错误的。我确实根据您的其他评论解决了这个问题,以消除噪音代码。谢谢!
    • @RobN:如果结构末尾没有填充,则没有区别。大步前进,您就安全了。这里stackoverflow.com/a/33177600/1187415 是一个(也许是异国情调的)使用 size 不起作用的案例。
    【解决方案3】:

    Swift 5 淘汰了withUnsafeBytes(UnsafePointer&lt;sockaddr&gt;),所以下面是我对 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)
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-07
      相关资源
      最近更新 更多