将字符串从Swift传递给C回到Swift

时间:2016-10-18 19:10:39

标签: c swift objective-c-swift-bridge

我在Swift和C之间传递字符串时看到一些我不明白的行为。请考虑以下Swift函数:

func demo()
{
    print("\n\n\n\n")                                                            // Line A

    let str = "thisisastring"
    let strptr = UnsafePointer<Int8>(str)        
    let strptr_cstring = String(cString: strptr)
    print("from swift: str = '\(str)'")
    print("from swift: strptr = \(strptr)")
    print("from swift: strptr.pointee = \(strptr.pointee)")
    print("from swift: strptr_cstring = '\(strptr_cstring)'")
    print("from swift: String(cString: strptr) = '\(String(cString: strptr))'")

    let ret_strptr = return_string(str)!                                        // Line B1
    //let ret_strptr = return_string(strptr)!                                   // Line B2
    //let ret_strptr = strptr                                                   // Line B3

    print("from swift: ret_strptr = \(ret_strptr) ")
    print("from swift: ret_strptr.pointee = \(ret_strptr.pointee)")
    let ret_strptr_cstring = String(cString: ret_strptr)
    print("from swift: ret_strptr_cstring = '\(ret_strptr_cstring)'")
    print("from swift: String(cString: ret_strptr) = '\(String(cString: ret_strptr))'")
}

C函数:

const char *return_string(const char *c)
{
    printf("from c: got pointer %p = %s\n",c,c);
    return c;
}

如果我在上面运行demo(因此只有B2和B3行被注释掉),我得到以下输出:

from swift: str = 'thisisastring'
from swift: strptr = 0x000000010021b8a0
from swift: strptr.pointee = 116
from swift: strptr_cstring = 'thisisastring'
from swift: String(cString: strptr) = 'thisisastring'
from c: got pointer 0x10021b9b0 = thisisastring
from swift: ret_strptr = 0x000000010021b9b0 
from swift: ret_strptr.pointee = 102
from swift: ret_strptr_cstring = '102m swift: ret_'
from swift: String(cString: ret_strptr) = 'P�!'

有两件事让我感到惊讶:1)C代码获取指针0x10021b9b0,而在Swift中,字符串存储在0x000000010021b8a0。我本来期望它们是相同的,但我猜Swift在将它传递给C之前制作了一个字符串的副本? 2)更令人惊讶的是,虽然从C返回到Swift的指针是相同的位置,但内容却不同;此外,最后两个印刷语句产生不同的输出。

现在,如果我只是在demo中注释掉A行,我会得到以下结果:

from swift: str = 'thisisastring'
from swift: strptr = 0x00000001003894b0
from swift: strptr.pointee = 0
from swift: strptr_cstring = 'S\212' 
from swift: String(cString: strptr) = ''
from c: got pointer 0x100469640 = thisisastring
from swift: ret_strptr = 0x0000000100469640 
from swift: ret_strptr.pointee = 48
from swift: ret_strptr_cstring = '48000010046964'
from swift: String(cString: ret_strptr) = '48000010046964'

这是令人惊讶的,因为调用C函数之前的所有内容都是错误的。

如果我然后注释掉B1行并取消注释B3(只是删除对C的调用),我得到预期的输出:

from swift: str = 'thisisastring'
from swift: strptr = 0x00000001004809b0
from swift: strptr.pointee = 116
from swift: strptr_cstring = 'thisisastring'
from swift: String(cString: strptr) = 'thisisastring'
from swift: ret_strptr = 0x00000001004809b0 
from swift: ret_strptr.pointee = 116
from swift: ret_strptr_cstring = 'thisisastring'
from swift: String(cString: ret_strptr) = 'thisisastring'

如果我用线B2运行它取消注释(注释掉A,B1和B3),我得到:

from swift: str = 'thisisastring'
from swift: strptr = 0x0000000100203550
from swift: strptr.pointee = 0
from swift: strptr_cstring = '2'
from swift: String(cString: strptr) = ''
from c: got pointer 0x100203550 = 
from swift: ret_strptr = 0x0000000100203550 
from swift: ret_strptr.pointee = 0
from swift: ret_strptr_cstring = ''
from swift: String(cString: ret_strptr) = ''

最后,如果我使用未注释的行A和B2运行(B1和B3注释掉),我会得到预期的结果:

from swift: str = 'thisisastring'
from swift: strptr = 0x00000001005533d0
from swift: strptr.pointee = 116
from swift: strptr_cstring = 'thisisastring'
from swift: String(cString: strptr) = 'thisisastring'
from c: got pointer 0x1005533d0 = thisisastring
from swift: ret_strptr = 0x00000001005533d0 
from swift: ret_strptr.pointee = 116
from swift: ret_strptr_cstring = 'thisisastring'
from swift: String(cString: ret_strptr) = 'thisisastring'

在我看来,对C的调用是以不可预测的方式踩内存,但我在这里做错了吗?

在macOS Sierra,Xcode 8.0,Swift 3.0上运行。

1 个答案:

答案 0 :(得分:0)

如果您想要来自NSString的C字符串,请使用cStringgetCString