【问题标题】:Swift: how add offset to memcpy(...)Swift:如何向 memcpy(...) 添加偏移量
【发布时间】:2014-09-24 14:34:06
【问题描述】:

如何为 memcpy(...) 调用添加数组偏移量?

我有 String 数组:

var source = ["a","b","c","d"]
var dest = [String](count:n, repeatedValue: "")
memcpy(&dest, source, UInt(2 * sizeof(String))

这个副本 ["a","b"] 到 dest。很明显。 我如何复制 ["b", "c"] ?

【问题讨论】:

  • 这样做的目的是什么?

标签: ios arrays performance swift memcpy


【解决方案1】:

我还是 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)

【讨论】:

  • 感谢您实际回答问题。是的,这是非常危险的,不应该使用,但这就是警告的用途。即使您发现零个合法用例,深入了解一下仍然很有趣。只是我的两分钱。
【解决方案2】:

首先,作者似乎都没有理解:对象数组(这里是 String 实例)不存储内容,而是对该对象的引用。因此 UTF-8、UTF-16 都与它无关。后备数组实际上包含的是指针(即地址 == 无符号整数)。除此之外,除非 swift 中的数组是内存中的实际数组,否则不应在其上使用 memcpy,如果它由 NSArray 支持则更是如此!

尽管如此,要回答似乎运行良好的原始问题并让我认为在这种情况下 Swift 数组是一个连续的内存区域,您应该这样做:

source 和 dest 是指向连续内存区域的指针:第一个对象位于基地址,第二个 @+sizeof(type),第 n 个元素位于 @+(n-1)*sizeof(type)。

您所要做的就是指定 dest 的写入偏移量,在您的特定情况下为 0,在源中的偏移量在您的情况下为 1。

【讨论】:

  • 但是 Swift 不允许你在 memcpy() 调用的操作数上指定偏移量。
  • 字符串是值类型,而不是引用。另一方面,尝试使用 memcpy 复制字符串,如果您设法以某种方式进行复制,则会复制这些位,但稍后会给您带来严重的麻烦。
  • 尽管我发现上面的答案大部分是有效的 AFAIK Array 不能保证是一个连续的内存区域,另一方面 ContiguousArray
  • @bartlomiej.n 你说得对,参考 Array 的文档,这取决于它的支持。通过 Swift,我的意思不是 NSArray。顺便说一句,感谢您提供的信息。
【解决方案3】:

不要对对象使用 memcpy 或其他低级“C”运算符。由于很多原因,这行不通。

使用切片运算符:

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

输出:

dest: [b, c]

Unicode 处理正确:

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

输出:

dest: [?, a]

【讨论】:

  • 感谢您的回答。它对我有很大帮助。这是一种很好且快速的方法,但仍然比 memcpy() 慢一点。
  • @user2245247 memcpy 不一定适用,当然也不适用于 Unicode 表情符号、代理对和字符为多个 UTF-16 单元的标志。虽然 String 的实现似乎是 UTF-16,但可能会发生变化。 Swift 代码比 memcpy 慢,因为它可以正确处理大小为多个 UTF-16 单位的字符。根据 Donald Knuth:“过早的优化是万恶之源”。
  • 您介意看看我下面的回答,如果您认为这太危险,请告诉我?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-25
  • 1970-01-01
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多