Reputation: 122
import Foundation
func address(o:UnsafeRawPointer) -> Int {
return Int(bitPattern: o)
}
var originArray = [1,2,3]
var firstArray = originArray
//q.append(4)
print(NSString.init(format: "originArray:%p", address(o: &originArray)))
print(NSString.init(format: "firstArray:%p", address(o: &firstArray)))
Debug Log: originArray:0x100b087b0 firstArray:0x100b088c0
Above, it's my test code.I think that I don't modify originArray which like appending or reduceing element. they should point same address.but why deference
Upvotes: 3
Views: 578
Reputation: 80781
Your code is printing the addresses of the array buffers (Array
is a special case when passing a value to a pointer parameter). However, in Swift 3, the compiler assumed that the presence of the &
operator meant that the buffer was being passed as mutable memory, so (unnecessarily) made it unique (by copying) before passing its pointer value, despite that pointer value being passed as an UnsafeRawPointer
. That's why you see different addresses.
If you remove the &
operator and pass the arrays directly:
func address(_ p: UnsafeRawPointer) {
print(p)
}
var originArray = [1, 2, 3]
var firstArray = originArray
address(originArray) // 0x00000000016e71c0
address(firstArray) // 0x00000000016e71c0
You'll now get the same addresses, as the compiler now assumes that address(_:)
will not modify the memory of the buffers passed, as they're being passed to an UnsafeRawPointer
parameter.
In Swift 4, this inconsistency is fixed, and the compiler no longer makes the buffer unique before passing its pointer values to an UnsafeRawPointer
parameter, even when using the &
operator, so your code exhibits expected behaviour.
Although, it's worth noting that the above method isn't guaranteed to produce stable pointer values when passing the same array to multiple pointer parameters.
From the Swift blog post "Interacting with C Pointers":
Even if you pass the same variable, array, or string as multiple pointer arguments, you could receive a different pointer each time.
I believe this guarantee cannot be met for arrays in two cases (there may be more):
If the array is viewing elements in non-contiguous storage
Swift's Array
can view elements in non-contiguous storage, for example when it is wrapping an NSArray
. In such a case, when passing it to a pointer parameter, a new contiguous buffer will have to be created, therefore giving you a different pointer value.
If the buffer is non-uniquely referenced when passed as mutable memory
As mentioned earlier, when passing an array to a mutable pointer parameter, its buffer will first be made unique in order to preserve value semantics, as it's assumed the function will perform a mutation of the buffer.
Therefore, if the buffer was copied, you'll get a different pointer value to if you had passed the array to an immutable pointer parameter.
Although neither of these two points are applicable in the example you give, it's worth bearing in mind that the compiler still doesn't guarantee you stable pointer values to the array's buffer when passing to pointer parameters.
For results that are guaranteed to be reliable, you should use the withUnsafeBytes(_:)
method on a ContiguousArray
:
var originArray: ContiguousArray = [1, 2, 3]
var firstArray = originArray
originArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000102829550
firstArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000102829550
This is because withUnsafeBytes(_:)
is documented as accepting:
A closure with an
UnsafeRawBufferPointer
parameter that points to the contiguous storage for the array. If no such storage exists, it is created.
And ContiguousArray
guarantees that:
[it] always stores its elements in a contiguous region of memory
And just like Array
, ContiguousArray
uses copy-on-write in order to have value semantics, so you can still use it to check when the array's buffer is copied upon a mutation taking place:
var originArray: ContiguousArray = [1, 2, 3]
var firstArray = originArray
originArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000103103eb0
firstArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000103103eb0
firstArray[0] = 4
originArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000103103eb0
firstArray.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000100e764d0
Upvotes: 5
Reputation: 42828
You're printing the address of the variable itself, not the address of the array buffer it's pointing to.
You can get the address of the arrays' buffers like so:
var originArray = [1, 2, 3]
var firstArray = originArray
print("originArray: \(originArray.withUnsafeBytes { $0.baseAddress! })")
print("firstArray: \(firstArray.withUnsafeBytes { $0.baseAddress! })")
Now the same value is printed, unless you modify one of the arrays.
Upvotes: 1