【问题标题】:Strange behavior of UnsafeMutablePointerUnsafeMutablePointer 的奇怪行为
【发布时间】:2019-01-23 22:01:42
【问题描述】:

我已经构建了一个小结构,它包含一个伪 C 字符串 UnsafeMutablePointer。现在我想添加一种方法来获取字符串的长度,但是有些东西表现得很奇怪:

struct Char {
    let value: CChar

    init(_ char: CChar) {
        self.value = char
    }
}

extension Char: CustomStringConvertible {
    var description: String {
        return String(UnicodeScalar(UInt8(bitPattern: self.value)))
    }
}

extension Char: Equatable {
    public static func ==(lhs: Char, rhs: Char) -> Bool {
        return lhs.value == rhs.value
    }
}

struct CharPointer {
    let pointer: UnsafeMutablePointer<Char>

    init(_ string: String) {
        let chars = string.cString(using: .utf8)!.map { Char($0) }
        self.pointer = UnsafeMutablePointer(mutating: chars)
    }

    subscript(_ offset: Int) -> Char {
        get { return self.pointer[offset] }
        set { self.pointer[offset] = newValue }
    }

    var length: Int {
        var ptr = self.pointer

        while ptr.pointee != Char(0) {
            ptr += 1
        }

        return ptr - self.pointer
    }
}

let pointer = CharPointer("Hello World!")

print(pointer[0])

// print(pointer.length)

如果我不使用length 属性,一切都会正常工作,并且打印语句会打印一个“H”。但是如果我尝试访问pointer.length,它会打印一个换行符,尽管我在打印语句之后使用了该属性,并且长度为0。这是一个错误还是我犯了错误?

【问题讨论】:

  • UnsafeMutablePointer(mutating: chars) 给你一个悬空指针,因为隐式参数转换产生的指针只在初始化程序调用期间有效——取消引用该指针会给你未定义的行为。真正的 Swift 应该禁止 UnsafePointer.init 参数的 inout-to-pointer 参数转换(我实际上正在研究实现它)。

标签: swift unsafe-pointers unsafemutablepointer


【解决方案1】:

感谢@Hamish,我找到了解决方案,我只是用数组中的指针初始化了第二个指针:

init(_ string: String) {
    let chars = string.cString(using: .utf8)!.map { Char($0) }
    let pointer = UnsafeMutablePointer(mutating: chars)
    let pointer2 = UnsafeMutablePointer<Char>.allocate(capacity: chars.count)
    pointer2.initialize(from: pointer, count: chars.count)

    self.pointer = pointer2
}

【讨论】:

  • 从技术上讲,这仍然会产生未定义的行为,因为您仍在转义临时指针参数 - 您可以通过直接说 pointer.initialize(from: chars, count: chars.count) 来避免这种情况。虽然现在您已经分配了内存,但您需要在某处释放它(否则您会出现内存泄漏)。您可以将CharPointer 设为class 并在deinit 中执行此操作——但为什么不在CharPointer 中存储Char 数组而不是UnsafeMutablePointer&lt;Char&gt;?然后 Swift 将为您处理缓冲区的内存,您可以保留 CharPointer 和 struct。
  • 哦,我也可以使用普通的 swift 字符串类型...不,我只是在使用 UnsafePointers 进行测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 2015-07-20
  • 2010-10-03
  • 2021-07-12
  • 2013-10-04
  • 2012-07-13
相关资源
最近更新 更多