2

The following unit test always fails as some UnsafePointer equal some other. This behavior I observe for at least 3 strings. I'm using Xcode Version 9.1 (9B55) on High Sierra Version 10.13.1 (17B48).

func testUnsafePointer() {
    let a = "aaa"
    let b = "bbb"
    let c = "ccc"

    let ua = UnsafePointer<Int8>(a)
    let ub = UnsafePointer<Int8>(b)
    let uc = UnsafePointer<Int8>(c)

    print("a: '\(a)', ua: '\(ua)'")
    print("b: '\(b)', ub: '\(ub)'")
    print("c: '\(c)', uc: '\(uc)'")

    XCTAssertTrue(ua != ub)
    XCTAssertTrue(ub != uc)
    XCTAssertTrue(uc != ua)
}

The printout looks similar to this

a: 'aaa', ua: '0x000060c000056a30'
b: 'bbb', ub: '0x00006080000530d0'
c: 'ccc', uc: '0x00006080000530d0'

NB. in the swift playground everything looks ok.

Hamish
  • 78,605
  • 19
  • 187
  • 280
swift616
  • 23
  • 3
  • `UnsafePointer(a)` almost certainly doesn't do what you think it does – Swift creates a new buffer with the null-terminated UTF-8 bytes of the given string, and then gives you a pointer to that buffer. The buffer is deallocated after the call ends, so you're left with a dangling pointer. So it's not out of the question for those pointers to share the same values. But because they're dangling pointers, attempting to dereference them is *undefined behaviour*. Whatever you're trying to do here is almost certainly not the right way of going about it. – Hamish Nov 14 '17 at 21:13
  • @Hamish this explains the behavior. What I intended to do is passing swift strings to a C function with this signature: void cfunc(const char *a, const char *b, const char *c). But in this cfunc just the first string is fine, the others are garbage. I call this c func with the UnsafePointer conversion. – swift616 Nov 14 '17 at 21:21
  • 2
    @swift616: You can simply call `cfunc(a, b, c)`, then the compiler automatically creates the necessary code for the conversion. – Martin R Nov 14 '17 at 21:22

1 Answers1

2

In

let ua = UnsafePointer<Int8>(a)

the

init(_ other: UnsafePointer<Pointee>)

initializer of UnsafePointer is called. I.e. you are passing a Swift String to a function taking a UnsafePointer<Int8> argument. In that case a temporary C string representation is created and passed to the function, compare String value to UnsafePointer<UInt8> function parameter behavior.

That temporary C string is only valid for the duration of the function call, and on return, it is not guaranteed that the memory pointed to by ua still contains that C string (or any valid data). In addition,

let ua = UnsafePointer<Int8>(a)
let ub = UnsafePointer<Int8>(b)
let uc = UnsafePointer<Int8>(c)

can use the same storage for the temporary C string, in that case the pointers would be equal, as you observed.


If your intention is to call a C function taking const char * arguments, then you can simply pass the Swift strings. The compiler will insert the necessary code to create C string representations (which are again valid for the duration of the function call), as explained in String value to UnsafePointer<UInt8> function parameter behavior:

let a = "aaa"
let b = "bbb"
let c = "ccc"

cfunc(a, b, c)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382