Почему, передавая элемент массива функции через ParamArray, выполняет ли varpointer функцию? - программирование
Подтвердить что ты не робот

Почему, передавая элемент массива функции через 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 не может принимать переменные массива.)

4b9b3361

Ответ 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

Это не ошибка, это случай, когда данные строго не вводятся.