Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Difference between passing variable and passing variable address withUnsafePointer?

Tags:

ios

swift

recently I was trying to see the address of my variable and I have this question.

var age: Int = 5

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

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

why it shows different memory address and why it shows the same value for the 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
}

for the array, why it shows the same memory address for index 1 and 2, when I'm not passing the address of the variable and why it shows a different memory address for index 1 and 2 when I'm passing the memory address?

Appreciate your answer and looking forward to understand this

like image 814
soer Avatar asked Mar 16 '26 18:03

soer


1 Answers

The differences you see come from the fact that you are using two different overloads of withUnsafePointer, I'll list them in the same order you used them:

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

, and

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

The difference between the two is the inout qualifier used for the value parameter.

Now, let's try to understand what happens behind the scenes.

First, 0x00007ffee3362750 looks like a stack pointer, while 0x000000010d226330 looks like a heap pointer. Stack addresses start at the top of the allocated memory for the program, and decrease with every function call (and increase when the function returns).

This indicates that the first overload of withUnsafePointer create a temporary writable variable from the one passed as the argument. This is needed as UnsafePointer needs an inout reference to work with, and a regular parameter is readonly.

This means that the implementation of the non-inout overload of withUnsafePointer looks something like this:

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)
}

Thus, the first call needs to allocate an intermediate memory location, and this is why you see two different addresses, it's because the addresses are not pointing to the same memory location. However, since both memory locations start with the same value, printing the pointee results in identical output.


Now, let's talk about the array example. The behaviour you see has the same cause: stack allocations. What happens is:

  1. program starts executing, stack pointer has value P (random name)
  2. non-inout withUnsafePointer is called, this is a function call, and stack is reserved for the function; stack pointer is P - N
  3. withUnsafePointer creates the temporary writable variable, and executes
  4. withUnsafePointer returns, and frees the stack memory, at this stack pointer gets back to P
  5. second non-inout withUnsafePointer is called, the stack pointer is back to P - N, however since there were no other function calls between the two, the same stack address is reserved for the temporary writable variable, thus the UnsafePointer instance has the same address

Here, even if the UnsafePointer points to the same address, the values at that address are different, corresponding to values of arr[0] and arr[1].

As for the inout calls, the UnsafePointer point to the actual addresses of the items in the array buffer.

This is how you can get different values for the non-inout calls too:

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()
like image 141
Cristik Avatar answered Mar 18 '26 08:03

Cristik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!