Почему, передавая элемент массива функции через ParamArray, выполняет ли varpointer функцию?
Я заметил странность в VBA при использовании ParamArray
и прохождении элементов массива через него. В некоторых случаях это не значение элемента массива, которое приходит в функцию, а указатель var. (Excel 2016, 32-бит).
После некоторого зависания я обнаружил, что определение функции как варианта массива - в сочетании с списком параметров в сочетании с ParamArray
- похоже, происходит, когда происходит непредвиденное поведение, но я не вижу никаких возможных причин, по которым это было бы так,
Поведение возвращается к нормальному, когда:
1) переменная r удаляется в объявлениях функций
2) b
объявляется с помощью Dim b()
3) функция возвращает Variant
, а не Variant()
Я ценю, что это довольно эзотерический вопрос, и он, по-видимому, контролируется по-разному, но есть ли объяснение, объясняющее это поведение?
Sub Variantarraybug()
Dim b: b = [{1, 2, 3}]
Debug.Print farray1(2, b(1))(0)
Debug.Print Application.WorksheetFunction.Sum(farray1(2, b(1)))
Debug.Print Join(farray1(2, b(1)), " ")
Debug.Print farray2(2, b(1))(0)
Debug.Print Application.WorksheetFunction.Sum(farray2(2, b(1)))
Debug.Print Join(farray2(2, b(1)), " ")
Debug.Print VarPtr(b(1)), VarPtr(b(2))
End Sub
Function farray1(r, ParamArray plop()) As Variant
farray1 = Array(plop(0), 3)
End Function
Function farray2(r, ParamArray plop()) As Variant()
farray2 = Array(plop(0), 5)
End Function
Результат в окне отладки:
1
4
1 3
1
6
358808368 5
358808368 358808384
Примечание 1: Я понимаю, что функция VarPtr
возвращает местоположение памяти начального адреса памяти, требуемого этой переменной. Здесь он используется только для того, чтобы показать, что неожиданный номер (358808368), который был замечен функцией farray2
, на самом деле является адресом этого элемента.
Примечание 2: Это происходит не зависит от того, как вы создаете массив (например, b=array(1,2,3)
, b=[1,2,3]
, и т.д.), и как b
декларируется (b
, b(1 to 3)
и т.д.). Однако, если вы объявите b
с помощью Dim b()
, неожиданное поведение исчезнет. (Вы не можете напечатать VarPtr(b)
в этом случае, поскольку VarPtr
не может принимать переменные массива.)
Ответы
Ответ 1
Вы сказали, что VarPtr не может принимать переменные массива. Да, это правда, но есть способы обойти это ограничение. Смотрите https://bytecomb.com/vba-internals-array-variables-and-pointers-in-depth/
Добавьте несколько строк кода, чтобы доказать, что вы можете получить - даже в VBA - адрес массива.
Private Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias "VarPtr" (ByRef Var() As Any) As LongPtr
Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest() As Any, pSource() As Any, ByVal ByteLen As Long)
Declare PtrSafe Sub CopyMemory0 Lib "kernel32" Alias "RtlMoveMemory" (pDest() As Any, pSource As Any, ByVal ByteLen As Long)
Option Explicit
Function farray1ck(r, ParamArray plop()) As Variant
farray1ck = Array(plop(0)(1), 3)
End Function
Sub VariantarrayIssue()
Dim b()
Dim c()
b = [{7, 2, 3}]
Dim lp As LongPtr
lp = VarPtrArray(b)
Debug.Print lp
lp = 0
CopyMemory c, b, LenB(lp)
Debug.Print farray1ck(2, c)(0)
CopyMemory0 c, lp, LenB(lp) 'Important to avoid crash due to clean up!!
End Sub
На C++ массив VBA равен SAFEARRAY ** (double *), что означает адресную память адреса массива. Я пытался увидеть, примет ли Excel вызов вышеупомянутой функции только со значением, как указатель, но это не так.
Я знаю, что на самом деле я не отвечаю на ваш вопрос, но, надеюсь, у вас или у всех, кого это интересует, может быть больше инструментов для исследования проблемы.
Ответ 2
Для меня это сводится к передаче неправильного типа данных.
Вам необходимо передать вариант в ParamArray https://docs.microsoft.com/en-us/office/vba/Language/Reference/user-interface-help/paramarray-must-be-declared-as-an-array- из-вариант
Изменение b (1) на вариант работает нормально.
Sub Variantarraybug()
Dim b: b = [{1, 2, 3}]
Debug.Print farray1(2, CVar(b(1)))(0)
Debug.Print Application.WorksheetFunction.Sum(farray1(2, CVar(b(1))))
Debug.Print Join(farray1(2, CVar(b(1))), " ")
Debug.Print farray2(2, CVar(b(1)))(0)
Debug.Print Application.WorksheetFunction.Sum(farray2(2, CVar(b(1))))
Debug.Print Join(farray2(2, CVar(b(1))), " ")
Debug.Print VarPtr(b(1)), VarPtr(b(2))
End Sub
Function farray1(r, ParamArray plop()) As Variant
farray1 = Array(plop(0), 3)
End Function
Function farray2(r, ParamArray plop()) As Variant()
farray2 = Array(plop(0), 5)
End Function
Это не ошибка, это случай, когда данные строго не вводятся.