I recently fixed a bug in some code handing off a string to a C library function. The C function takes a plain
char * (no
const in either position):
void use_str(char * s); so we need an
UnsafeMutablePointer<CChar> on the Swift side. Our Swift code was basically this:
import Foundation let s = "abcde" let sPtr = UnsafeMutablePointer(mutating: s.cString(using: .utf8)) use_str(sPtr)
Running this, if you inspect
sPtr.pointee before the
use_str call, you can see that the pointer is bad: it does not point to the contents of the string.* Until Xcode 10/Swift 4.2, it had consistently pointed to zeroed-out memory and the original implementer "worked around" what they thought was a bug in the C library. When we upgraded, the pointed-to values started being garbage, and there was breakage.
Storing the C string in a local variable first,
var sChars = s.cString(using: .utf8)!, fixes the issue. (That done, we can apparently also skip the explicit call to
UnsafeMutablePointer(mutating:) and just write
So it looks like the return value of
cString(using:) is invalid immediately unless explicitly copied. Shouldn't it live as long as the
String that produced it? If I understand correctly what's happening, this ObjC is equivalent to the original code:
NSString * s = @"abcde"; use_str([s cStringUsingEncoding:NSUTF8StringEncoding]);
which is perfectly valid, as far as I know (
use_str may need to copy the bytes, of course, but that's a separate issue).
Alternatively, can/should there be a warning about
UnsafeMutablePointer(mutating:) pointing directly to the result of a method call like this? Maybe our original Swift code is more equivalent instead to this, taking the address of a message send expression, which is illegal:
void update_char(char * c); //... update_char(&[s characterAtIndex:0]);
Please help me correct errors in my understanding; I'm trying to better grasp the situation/how Swift pointers operate. We should have caught this bug in our code, but I'm not sure why it occurred in the first place.
*In fact if you add another string/pointer pair, in Swift 4.2 the two pointers consistently hold the same address!