We could see, when it is string, the output of the first two addresses are the same.
what is the third address of the string without &?
But when it is an array, the output of the second and the third lines are the same address, but the first line is different, what is this address and why?
In order to make C interoperation easier, Swift does some special casing when passing strings or arrays to functions that take pointer parameters. Unfortunately, as you've seen, the behavior can be surprising and inconsistent.
What's happening is that when passing a string or array as a pointer parameter, you are sometimes passing the location of the string or array's data and sometimes passing the location of the local variable, which is almost never what you want. Unpleasantly, the same syntax has different behavior for the two types. Both versions of the call below pass a pointer to the array's storage, but only one does that for the string:
printMemoryAddress(&str) // passes a pointer to `str`, the variable
printMemoryAddress(str) // passes a pointer to the storage of `str`*
// the first syntax is required if the pointer is mutable:
printMemoryAddress(&arr) // passes a pointer to the storage of `arr`
printMemoryAddress(arr) // passes a pointer to the storage of `arr`
For both types, calling withUnsafePointer(to:) gives you access to the location of the local variable, not the data it represents. Instead of using that function, each of those types has methods that you can use to access their contents as a pointer (be sure to read the documentation for all methods that provide unsafe access to memory):
*As a final note, since strings can have different representations internally, passing a string with the printMemoryAddress(str) syntax can require a copy that ends up getting discarded immediately after the call, which can have performance impacts.
There used to be an article in the Swift documentation that covered these conventions, but I can't seem to find it anymore! If I come across it, I'll add it here.
var str = "what is that?"
func printChar(_ pointer: UnsafeRawPointer) {
var p = pointer.assumingMemoryBound(to: CChar.self)
var buffer = String()
while true {
let c = p.pointee
let uC = UnicodeScalar(UInt8(c))
buffer.append(uC.description)
if p.pointee == 0 {
break
}
p = p.advanced(by: 1)
}
print(buffer)
}
printChar(&str)
printChar(str)
/*
It prints:
what is that?
what is that?
*/
Ah, good catch! That's because when strings are small, Swift's String type stores its data inline, so the data and local variable are at the same memory location. If you increase the length of your string (past 14 UTF8 code units on 64-bit platforms, I believe), then the string will use a separate allocation for its data, and you should see the difference in behavior.