Swift:如何向memcpy(...)添加偏移量

7
如何为memcpy(...)调用添加数组偏移量?
我有一个String类型的数组:
var source = ["a","b","c","d"]
var dest = [String](count:n, repeatedValue: "")
memcpy(&dest, source, UInt(2 * sizeof(String))

将["a","b"]复制到目标位置很明显,那么如何复制["b", "c"]呢?


这样做的目的是什么? - fnc12
3个回答

8

不要在对象上使用memcpy或其他低级"C"运算符,这样做会有很多问题。

使用切片操作符:

var source = ["a","b","c","d"]
var dest = Array(source[1...2])
println("dest: \(dest)")

输出:

目的地:[b, c]

Unicode 被正确处理:

var source = ["", "", "a","b","c","d"]
var dest = Array(source[1...2])
println("dest: \(dest)")

输出:

目标:[, a]


谢谢你的回答。它对我很有帮助。这是一个好的和快速的方法,但仍然比memcpy()慢一点。 - Vitaliy L
@user2245247 memcpy不一定能够正常工作,特别是对于Unicode表情符号、代理对和旗帜等多个UTF-16单元的字符。虽然String的实现似乎是UTF-16,但这可能会改变。Swift代码比memcpy慢,因为它可以正确处理多个UTF-16单元大小的字符。正如Donald Knuth所说:“过早地优化是万恶之源”。 - zaph
您介意看一下我下面的答案,并告诉我您是否认为这太危险了吗? - RenniePet

3

我还不太熟悉Swift语言,带有“Unsafe”名称的方法仍然让我感到担忧,但我相当确定这是一种可用的调用memcpy()函数并指定目标和/或源地址偏移量的技术。但是,这仅适用于字节数组,即[UInt8]。绝对不能用于字符串,正如@zaph所解释的那样。

public class SystemMisc {

   /// Wrapper for the memcpy() method that allows specification of an offset for the destination
   /// and/or the source addresses.
   ///
   /// This version for when destination is a normal Swift byte array.
   ///
   /// - Parameters:
   ///   - destPointer:    Address for destination byte array, typically Swift [UInt8].
   ///   - destOffset:     Offset to be added to the destination address, may be zero.
   ///   - sourcePointer:  Address for source byte array, typically Swift [UInt8].
   ///   - sourceOffset:   Offset to be added to the source address, may be zero.
   ///   - byteLength:     Number of bytes to be copied.
   public static func memoryCopy(_ destPointer : UnsafeRawPointer, _ destOffset : Int,
                                 _ sourcePointer : UnsafeRawPointer, _ sourceOffset : Int,
                                 _ byteLength : Int) {

      memoryCopy(UnsafeMutableRawPointer(mutating: destPointer), destOffset,
                 sourcePointer, sourceOffset, byteLength)
   }


   /// Wrapper for the memcpy() method that allows specification of an offset for the destination 
   /// and/or the source addresses.
   ///
   /// This version for when destination address is already available as an UnsafeMutableRawPointer, 
   /// for example if caller has used UnsafeMutableRawPointer() to create it or is working with 
   /// unmanaged memory. The destPointer argument may also be a converted pointer, as done by the 
   /// above wrapper method.
   ///
   /// - Parameters:
   ///   - destPointer:    Address for destination byte array, see above notes.
   ///   - destOffset:     Offset to be added to the destination address, may be zero.
   ///   - sourcePointer:  Address for source byte array, typically Swift [UInt8].
   ///   - sourceOffset:   Offset to be added to the source address, may be zero.
   ///   - byteLength:     Number of bytes to be copied.
   public static func memoryCopy(_ destPointer : UnsafeMutableRawPointer, _ destOffset : Int,
                                 _ sourcePointer : UnsafeRawPointer, _ sourceOffset : Int,
                                 _ byteLength : Int) {

      memcpy(destPointer.advanced(by: destOffset),
             sourcePointer.advanced(by: sourceOffset),
             byteLength)
   }
}

以下是一些测试代码:

  // Test the memoryCopy() method, using extra UnsafeMutableRawPointer conversion

  let destArray1 : [UInt8] = [ 0, 1, 2, 3 ]  // Note - doesn't need to be var
  let sourceArray1 : [UInt8] = [ 42, 43, 44, 45 ]

  SystemMisc.memoryCopy(destArray1, 1, sourceArray1, 1, 2)

  assert(destArray1[0] == 0 && destArray1[1] == 43 && destArray1[2] == 44 && destArray1[3] == 3)


  // Test the memoryCopy() method, providing UnsafeMutableRawPointer for destination

  var destArray2 : [UInt8] = [ 0, 1, 2, 3 ]
  let sourceArray2 : [UInt8] = [ 42, 43, 44, 45 ]

  let destArray2Pointer = UnsafeMutableRawPointer(&destArray2)

  SystemMisc.memoryCopy(destArray2Pointer, 1, sourceArray2, 1, 2)

  assert(destArray2[0] == 0 && destArray2[1] == 43 && destArray2[2] == 44 && destArray2[3] == 3)

感谢您实际回答问题。是的,这非常危险,不应该使用,但这就是警告的用途。即使您找不到任何合法的用例,看看引擎底层仍然很有趣。仅代表个人观点。 - imas145

0
首先,似乎没有一个作者理解:对象数组(这里是字符串实例)不存储内容,而是存储对该对象的引用。因此,UTF-8、UTF-16或其他编码方式与此无关。支持数组实际包含的是指针(即地址==无符号整数)。除此之外,如果Swift中的数组不是实际的内存数组,则不应在其上使用memcpy,尤其是如果它由NSArray支持!
尽管如此,为了回答原始问题,似乎完美地工作,并且让我认为在这种情况下Swift数组是一段连续的内存区域,以下是您应该做的:
源和目标是指向连续内存区域的指针:第一个对象位于基地址处,第二个@+sizeof(type),第n个元素位于@+(n-1)* sizeof(type)。
您需要做的就是指定dest的写入偏移量,在您的特定情况下为0,以及源中的偏移量,在您的情况下为1。

但是 Swift 不允许您在 memcpy() 调用的操作数上指定偏移量。 - RenniePet
字符串是值类型,而不是引用。另一方面,如果你尝试使用memcpy复制一个字符串,即使你设法做到了,它也会复制位,但稍后会给你带来严重的麻烦。 - gnasher729
尽管我认为上面的答案大多数情况下是有效的,但据我所知,Array并不能保证是一个连续的内存区域,而ContiguousArray则可以。 - bartlomiej.n
@bartlomiej.n 你说得对,它取决于它的支持文档。我所指的Swift并不是指NSArray。顺便说一下,谢谢你提供的信息。 - Renaud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接