Очистка массива (так, что UBound(a) = -1)

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 03.10.2012 (Ср) 14:34

Для массивов пользовательских типов не годится, но для массивов классов и стандартных типов - вполне.
Код: Выделить всё
Public Sub EmptyArray(a, Optional Dims As Long = 1)
  Dim ArrayVar As Long, ArrayDesc As Long, Bounds As SAFEARRAYBOUND
  ArrayVar = Mem(VarPtr(a) + 8)
  ArrayDesc = Mem(ArrayVar)
  If ArrayDesc = 0 Then
    Mem(ArrayVar) = SafeArrayCreate(VarType(a) And (Not vbArray), Dims, Bounds)
  Else
    SafeArrayRedim ArrayDesc, Bounds
  End If
End Sub

Можно прописать для отдельных типов:
Код: Выделить всё
Public Sub EmptyByteArray(a() As Byte, Optional Dims As Long = 1)
  Dim ArrayVar As Long, ArrayDesc As Long, Bounds As SAFEARRAYBOUND
  ArrayVar = ArrPtr(a)
  ArrayDesc = Mem(ArrayVar)
  If ArrayDesc = 0 Then
    Mem(ArrayVar) = SafeArrayCreate(vbByte, Dims, Bounds)
  Else
    SafeArrayRedim ArrayDesc, Bounds
  End If
End Sub

Пояснения:
Mem(VarPtr(a) + 8 ) - чтение адреса переменной из типа Variant, смещение = 8 байтам
Mem(ArrayVar) - чтение адреса структуры SAFEARRAY, 0 - если массив не инициализирован
VarType(a) And (Not vbArray) - возращает тип элементов массива
Bounds As SAFEARRAYBOUND - .cElements = 0, .lLbound = 0
Вложения
TLB.zip
(63.27 Кб) Скачиваний: 97
Последний раз редактировалось Filyus 01.05.2013 (Ср) 10:11, всего редактировалось 7 раз(а).

Qwertiy
Доктор VB наук
Доктор VB наук
 
Сообщения: 2753
Зарегистрирован: 26.06.2011 (Вс) 21:26

Сообщение Qwertiy » 03.10.2012 (Ср) 15:02

Не понял, а в чём смысл данного кода? Т. е. для чего он нужен?

Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 03.10.2012 (Ср) 17:55

Qwertiy писал(а):Не понял, а в чём смысл данного кода? Т. е. для чего он нужен?

Чтобы не проверять массив на инициализированность. Т.е. чтобы вместо этого:
Код: Выделить всё
Private Sub Form_Load()
  Dim a() As Long
  Randomize Timer
  For i = 1 To CLng(Rnd * 2)
    ReDim Preserve a(i)
    a(i) = Rnd * 100
  Next i
  If SAPtr(a) <> 0 Then
    For i = 1 To UBound(a)
      Debug.Print a(i)
    Next i
  End If
End Sub

писать так:
Код: Выделить всё
Private Sub Form_Load()
  Dim a() As Long
  EmptyArray a
  Randomize Timer
  For i = 1 To CLng(Rnd * 2)
    ReDim Preserve a(i)
    a(i) = Rnd * 100
  Next i
  For i = 1 To UBound(a)
    Debug.Print a(i)
  Next i
End Sub

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Хакер » 03.10.2012 (Ср) 19:13

Идеологически верно иметь не такую функцию, а иметь функцию ArySize, возвращающую кол-во элементов в массиве.
Название функции никуда не годное.
Трюк с обёртыванием массива внутрь Variant-а — не очень хороший.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Viper » 03.10.2012 (Ср) 19:46

ЕМНИП, у нас есть аж два кирпича посвященные работе с массивами.
Весь мир матрица, а мы в нем потоки байтов!

Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 03.10.2012 (Ср) 20:23

Viper писал(а):ЕМНИП, у нас есть аж два кирпича посвященные работе с массивами.

и где эти "кирпичи"?

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Viper » 04.10.2012 (Чт) 4:50

Весь мир матрица, а мы в нем потоки байтов!

Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 04.10.2012 (Чт) 23:05

Viper писал(а):Раз и два.

Их я уже видел. Они тут не причём.

ark
Бывалый
Бывалый
 
Сообщения: 216
Зарегистрирован: 18.07.2011 (Пн) 0:57

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение ark » 06.10.2012 (Сб) 10:12

Простая подмена указателей прошла бы (возможно), если массив изначально был бы VT_BYREF. А так, в VarPtr(a)+8 надо всю структуру SAFEARRAY копировать:
Код: Выделить всё
Public Function EmptyArray(ByRef TheArray As Variant, Optional vt As VbVarType = vbEmpty) As Boolean
   Dim saBound As SAFEARRAYBOUND
   If vt = vbEmpty Then vt = VarType(TheArray)
   vt = vt And Not vbArray
   If vt <= vbNull Then vt = vbVariant
   Dim pSA As Long
   pSA = SafeArrayCreate(vt, 1, saBound)
   If pSA = 0 Then Exit Function
   vt = vt Or vbArray
   CopyMemory ByVal VarPtr(TheArray), vt, 2
   CopyMemory ByVal VarPtr(TheArray) + 8, pSA, Len(saBound) + 16
   EmptyArray = True
End Function

Ну и объявляй массив как вариант, а уж тип ставь в EmptyArray
Код: Выделить всё
Private Sub Command1_Click()
   Dim a As Variant
   If EmptyArray(a, vbLong) Then
      Debug.Print LBound(a)
      Debug.Print UBound(a)
   End If
End Sub

ЗЫ А вообще-то, ИМХО, правильнее написать какой-нить UBoundEx, возвращающий -1 для неинициализированного массива и пользовать его For i=1 To UboundEx(a)

Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 16.10.2012 (Вт) 8:39

Так что ли?)
Код: Выделить всё
Public Function UboundEx(Ar, Optional UnInitedValue = -1) As Long
  Dim n As Long
  n = Mem(VarPtr(Ar) + 8)
  If (MemInt(VarPtr(Ar)) And VT_BYREF) <> 0 Then n = Mem(n)
  If n <> 0 Then
    n = n + ((MemInt(n + SafeArrayDims) - 1) * 8)
    UboundEx = Mem(n + SafeArrayElements1D) + Mem(n + SafeArrayLbound1D) - 1
  Else
    UboundEx = UnInitedValue
  End If
End Function

Код: Выделить всё
Sub Main()
  Dim a(1), b(2) As Long, c() As Long, d(4, 6, 8)
  a(0) = b
  a(1) = с
  Debug.Print UboundEx(a) 'VT_BYREF
  Debug.Print UboundEx(a(0))
  Debug.Print UboundEx(a(1))
  Debug.Print UboundEx(с)
  Debug.Print UboundEx(d), UBound(d)
End Sub
Вложения
TLB.zip
(65.84 Кб) Скачиваний: 61
Последний раз редактировалось Filyus 16.10.2012 (Вт) 14:05, всего редактировалось 3 раз(а).

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Хакер » 16.10.2012 (Вт) 9:46

Filyus, твой код — полная гадость.
Не должно быть никаких Mem(n + загадочноечисло) в коде. Не должно быть никакого вариант-враппера.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 16.10.2012 (Вт) 10:20

Хакер писал(а):Filyus, твой код — полная гадость.
Не должно быть никаких Mem(n + загадочноечисло) в коде. Не должно быть никакого вариант-враппера.

Спасибо, я старался. А по правде, такой код написан для максимальной скорости его выполнения, а не чтения программистом. При желании можно вместо чисел ввести константы, а вместо враппера...
Если только для каждого типа прописывать...
Код: Выделить всё
Public Function UboundExLng(Ar() As Long, Optional UnInitedValue = -1) As Long
  Dim n As Long
  n = SAPtr(Ar)
  If n <> 0 Then
    n = n + ((MemInt(n + SafeArrayDims) - 1) * 8)
    UboundExLng = Mem(n + SafeArrayElements1D) + Mem(n + SafeArrayLbound1D) - 1
  Else
    UboundExLng = UnInitedValue
  End If
End Function

А ArrPtr и SAPtr для последних трёх строчек не работают...
Код: Выделить всё
Sub Main()
  Dim a(1), b(2) As Long, c() As Long
  a(0) = b
  a(1) = c
  Debug.Print UboundEx(SAPtr(a)) 'VT_BYREF
  Debug.Print UboundEx(SAPtr(a(0)))
  Debug.Print UboundEx(SAPtr(a(1)))
  Debug.Print UboundEx(SAPtr(c))
End Sub

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Хакер » 16.10.2012 (Вт) 16:43

Filyus писал(а):А ArrPtr и SAPtr для последних трёх строчек не работают...

Что такое ArrPtr и SAPtr?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Filyus
Обычный пользователь
Обычный пользователь
 
Сообщения: 81
Зарегистрирован: 07.07.2011 (Чт) 11:54

Re: Очистка массива (так, что UBound(a) = -1)

Сообщение Filyus » 17.10.2012 (Ср) 4:09

SAPtr - оболочка для GetMem4, получает или меняет адрес структуры SAFEARRAY, ArrPtr - оболочка для VarPtr, возращает адрес переменной, которая указывает на эту структуру.

Код: Выделить всё
[entry("GetMem4"), propget]
      HRESULT __stdcall SAPtr(
         [in]          SAFEARRAY(void) *   Array,
         [out, retval]   long *            lpRetVal
      );
[entry("PutMem4"), propput,]
      HRESULT __stdcall SAPtr(
         [in]          SAFEARRAY(void) *   Array,
         [in]          long               NewValue
      );

Код: Выделить всё
[entry("VarPtr")]
      long __stdcall ArrPtr([in] SAFEARRAY(void) * Ptr);


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Google-бот, Yandex-бот и гости: 71

    TopList