Swift array element address

2019-06-24 08:40发布

问题:

This is a practical question, i'm sending a file by breaking it into trunks of, say, 1000 bytes

data = NSData.dataWithContentsOfFile(path)
var dataPackage : [Byte](count: 1000, repeatedValue: 0)

for offset = 0; offset < data.length; {
     // omit some range check here
     data.getBytes(&dataPackage, NSRange(location: offset, length: 1000))
     send(dataPackage)
}

Everything was great, until I want to insert a sequence number into dataPackage, at position 0, so naturally, I would change the above to

data.getBytes(&dataPackage[1], NSRange(location: offset, length: 999))

It turned out that only 1 single element is copied to dataPackage. The rest 999 elements were copied to dont-know-where

My question is, 1) how to make this work, and 2) how array in swift is addressed, in such a way that &data[i] = &data + i (as shown in the 1st example) but &data[i+k] != &data[i] + k

Edit: I solved (1) by doing

data.getBytes(&dataPackage + 1, NSRange(location: offset, length: 999))

Question (2) remains

回答1:

Do not getBytes(&dataPackage[i] + k, ..), I think, this may causes "access violation"

Consider this code:

struct MyStruct {
    var _val = [Int](count: 1000, repeatedValue: 0);
    subscript(idx:Int) -> Int {
        get {
            println("get: [\(idx)] -> val(\(_val[idx]))")
            return _val[idx]
        }
        set(val) {
            println("set: [\(idx)] old(\(_val[idx])) -> new(\(val))")
            _val[idx] = val
        }
    }
}

func myFunc(ptr:UnsafeMutablePointer<Int>, val:Int) {
    println("mutating: ptr(\(ptr)) <- \(val)")
    ptr.memory = val
}

MyStruct is just a wrapper around Array.

myFunc receives a mutable pointer and mutates the value of it.

With this, when yo do:

var foo = MyStruct()
myFunc(&foo[1], 12)

outputs:

get: [1] -> val(0)
mutating: ptr(0x00007fff561619d8) <- 12
set: [1] old(0) -> new(12)

See?

  1. Copy the value to somewhere
  2. Mutate the value of somewhere
  3. Write-back from somewhere

I don't know where somewhere is, though.

When you do like:

var foo = MyStruct()
myFunc(&foo[1] + 1, 12)

outputs:

get: [1] -> val(0)
set: [1] old(0) -> new(0)
mutating: ptr(0x00007fff5c0b79e0) <- 12

This time, my guess would be:

  1. Copy the value to somewhere
  2. let corrupted as somewhere + 1
  3. Write-back with unmutated value from somewhere
  4. Discard somewhere
  5. Mutate the value of corrupted

On the other hand, when you do:

var foo = [Int](count:1000, repeatedValue:0)
myFunc(&foo + 1, 12)

This means:

  1. let ptr as the pointer of the first element of foo
  2. let ptr as ptr + 1
  3. Mutate the value of ptr

As a result, foo[1] will be 12 successfully as you did.

Actually in-out(&) of Array is very special as mentioned in the document.

When a function is declared as taking an UnsafeMutablePointer argument, it can accept any of the following:

  • nil, which is passed as a null pointer
  • An UnsafeMutablePointer value
  • An in-out expression whose operand is a stored lvalue of type Type, which is passed as the address of the lvalue
  • An in-out [Type] value, which is passed as a pointer to the start of the array, and lifetime-extended for the duration of the call

Only the built-in Array has the last feature.

So, when you pass &notAnArrayLvalue(including &array[i]) to UnsafeMutablePointer<Type> parameter, the length should be always 1, and you should not + or - it.