繁体   English   中英

Swift - 使用UnsafePointer传递变量和传递变量地址的区别?

[英]Swift - Difference between passing variable and passing variable address withUnsafePointer?

最近我试图查看我的变量的地址,但我有这个问题。

var age: Int = 5

withUnsafePointer(to: age) {
    print($0) // 0x00007ffee3362750
    print($0.pointee) // 5
}

withUnsafePointer(to: &age) {
    print($0) // 0x000000010d226330
    print($0.pointee) // 5
}

为什么它显示不同的 memory 地址以及为什么它为pointee显示相同的值?

var strarr = [1, 2]

withUnsafePointer(to: strarr[0]) {
    print("\($0)") // 0x00007ffee3362750
    print("\($0.pointee)") // 1
}

withUnsafePointer(to: strarr[1]) {
    print("\($0)") // 0x00007ffee3362750
    print("\($0.pointee)") // 2
}

withUnsafePointer(to: &strarr[0]) {
    print("\($0)") // 0x0000600002755760
    print("\($0.pointee)") // 1
}

withUnsafePointer(to: &strarr[1]) {
    print("\($0)") // 0x0000600002755768
    print("\($0.pointee)") // 2
}

对于数组,为什么当我没有传递变量的地址时它为索引 1 和 2 显示相同的 memory 地址,为什么当我传递 memory 地址时它为索引 1 和 2 显示不同的 memory 地址?

感谢您的回答并期待了解这一点

您看到的不同之处在于您使用了withUnsafePointer的两个不同重载,我将按照您使用它们的相同顺序列出它们:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

, 和

func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

两者之间的区别在于用于value参数的inout限定符。

现在,让我们尝试了解幕后发生的事情。

首先, 0x00007ffee3362750看起来像一个堆栈指针,而0x000000010d226330看起来像一个堆指针。 堆栈地址从为程序分配的 memory 的顶部开始,并随着每次 function 调用而减少(并在 function 返回时增加)。

这表明withUnsafePointer的第一个重载从作为参数传递的变量创建了一个临时的可写变量。 这是必需的,因为UnsafePointer需要一个inout引用才能使用,并且常规参数是只读的。

这意味着withUnsafePointer的非 inout 重载的实现看起来像这样:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result {
    var value = value // shadow the argument, create a readwrite location
    return try withUnsafePointer(&value, body)
}

因此,第一个调用需要分配一个中间位置 memory,这就是您看到两个不同地址的原因,这是因为地址没有指向相同的 memory 位置。 但是,由于两个 memory 位置都以相同的值开始,因此打印指向pointee会得到相同的 output。


现在,让我们谈谈数组示例。 您看到的行为具有相同的原因:堆栈分配。 发生的事情是:

  1. 程序开始执行,堆栈指针的值为 P(随机名称)
  2. non-inout withUnsafePointer被调用,这是一个function的调用,栈是为function预留的; 堆栈指针是 P - N
  3. withUnsafePointer创建临时可写变量,并执行
  4. withUnsafePointer返回,并释放堆栈 memory,此时堆栈指针回到 P
  5. 第二次调用 non-inout withUnsafePointer时,堆栈指针返回 P - N,但是由于两者之间没有其他 function 调用,因此为临时可写变量保留了相同的堆栈地址,因此UnsafePointer实例具有相同的地址

这里,即使UnsafePointer指向同一个地址,该地址的值也是不同的,对应于arr[0]arr[1]的值。

对于 inout 调用, UnsafePointer指向数组缓冲区中项目的实际地址。

这也是您可以为非 inout 调用获取不同值的方式:

withUnsafePointer(to: strarr[0]) {
    print("\($0)")
    print("\($0.pointee)")
}

// this adds a nested function call, which also decreases the stack pointer
// resulting in the temporary location not being on the same stack address
func test() {
    withUnsafePointer(to: strarr[1]) {
        print("\($0)")
        print("\($0.pointee)")
    }
}
test()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM