5

I have a c function which I access from a bridging header that returns a const char*:

const char* get_c_string();

I then try to convert it to a Swift String:

let str = String.fromCString(UnsafePointer<Int8>(get_c_string()))
print(str)

... but it just prints garbage:

Optional("\u{14}\0\0")

I can find how to pass Swift string to C but not the other way around. How can I convert a const char* to a Swift String?

Thanks!

Pat Mustard
  • 1,852
  • 9
  • 31
  • 58
  • This string has special characters ? – Alvaro Silvino Oct 08 '15 at 14:23
  • No it's just a standard `const char*` c string (from a `std::string`) – Pat Mustard Oct 08 '15 at 14:25
  • You have two *different* functions `get_c_string` and `call_hi` in your examples. Apart from that, your conversion method should work. Note that the pointer conversion is not necessary: `let str = String.fromCString(get_c_string())`. – Martin R Oct 08 '15 at 14:27
  • typo - the call matched the prototype so am calling the correct function. It looks like an encoding issue but not sure what. – Pat Mustard Oct 08 '15 at 14:32
  • Perhaps get_c_string() returns the contents of a local variable (which becomes invalid when the function returns)? You must ensure that the returned pointer is still valid on function return (e.g. malloc memory or strdup the string). Your Swift code is correct. – Martin R Oct 08 '15 at 14:32
  • Yup - you got it! My c is rustier than I thought o_O Do you want to put that in an answer for me to accept? – Pat Mustard Oct 08 '15 at 14:36

1 Answers1

16

Your Swift code is correct, you can shorten it slightly to

// Swift 2:
let str = String.fromCString(get_c_string())
// Swift 3:
let str = String(cString: get_c_string())

However, you must ensure that the pointer returned from the C function is still valid when the function returns. As an example

const char* get_c_string() {
    char s[] = "abc";
    return s;
}

returns the address of a local stack variable, which is undefined behavior.

You might have to duplicate the string (and then think of when and where to release the memory).

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • This API usage is no longer current. Compiler reports: "Please use String.init?(validatingUTF8:) instead. Note that it no longer accepts NULL as a valid input. Also consider using String(cString:), that will attempt to repair ill-formed code units." – sandover Mar 29 '16 at 01:47
  • @sandover: The above code still compiles without warnings or errors with Xcode 7.3/Swift 2.2, which is the current release version. The API seems to change in Swift 3, so I'll have to updated the answer once that becomes the official release. – Martin R Mar 29 '16 at 08:51
  • I was hoping this was the problem I'm having, but I don't think it is. If you have similar insight here, would like to hear your thoughts: http://stackoverflow.com/questions/37776867/swift-string-from-c-string-has-correct-length-but-incorrect-contents – johnbakers Jun 12 '16 at 17:02