visual type net miembros dim data convertir vba

vba - type - miembros de integer



Entero vs larga confusiĆ³n (7)

"En versiones recientes, sin embargo, VBA convierte todos los valores enteros a tipo Long, incluso si se declaran como tipo Integer".

No creo en esa documentación. Considere el siguiente ejemplo simple (ejecutado en Excel 2010):

Sub checkIntegerVsLong() ''Check the total memory allocation for an array Dim bits As Integer ''or long? :) Dim arrInteger() As Integer ReDim arrInteger(1 To 5) arrInteger(1) = 12 arrInteger(2) = 456 ''get total memory allocation for integer in array bits = VarPtr(arrInteger(2)) - VarPtr(arrInteger(1)) Debug.Print "For integer: " & bits & " bits and " & bits * 8 & " bytes." Dim arrLong() As Long ReDim arrLong(1 To 5) arrLong(1) = 12 arrLong(2) = 456 ''get memory allocation for long bits = VarPtr(arrLong(2)) - VarPtr(arrLong(1)) Debug.Print "For long: " & bits & " bits and " & bits * 8 & " bytes." End Sub

Esto imprime:

Para entero: 2 bits y 16 bytes.

Para largo: 4 bits y 32 bytes.

También puede probar esto en variables individuales usando lo siguiente:

Sub testIndividualValues() Dim j As Long Dim i As Integer Dim bits As Integer bits = LenB(i) Debug.Print "Length of integer: " & bits & " bits and " & bits * 8 & " bytes." bits = LenB(j) Debug.Print "Length of long: " & bits & " bits and " & bits * 8 & " bytes." End Sub

que imprime

Longitud del entero: 2 bits y 16 bytes.

Longitud de largo: 4 bits y 32 bytes.

Por último, puede usar una comparación de tipos aquí:

Public Type myIntegerType a As Integer b As Integer End Type Public Type myLongType a As Long b As Long End Type Public Sub testWithTypes() Dim testInt As myIntegerType Dim testLong As myLongType Dim bits As Integer bits = VarPtr(testInt.b) - VarPtr(testInt.a) Debug.Print "For integer in types: " & bits & " bits and " & bits * 8 & " bytes." bits = VarPtr(testLong.b) - VarPtr(testLong.a) Debug.Print "For long in types: " & bits & " bits and " & bits * 8 & " bytes." End Sub

que también imprime:

Para enteros en tipos: 2 bits y 16 bytes.

Para largos en tipos: 4 bits y 32 bytes.

Esta es una evidencia bastante convincente para mí de que VBA realmente trata a Integer y Long diferente.

Si VBA convierte silenciosamente detrás de escena, es de esperar que devuelvan el mismo número de bits / bytes para cada una de las ubicaciones de asignación de puntero. Pero en el primer caso, con Integer, solo asigna 16 bits, mientras que para las variables largas, asigna 32 bits.

¿Y qué?

Entonces a su pregunta de

Si VBA convierte todos los valores Integer en tipo Long, incluso si se declaran como tipo Integer, ¡lo anterior nunca debería dar el error de desbordamiento!

Tiene mucho sentido que obtenga un error de desbordamiento, ya que VBA no ha asignado realmente la memoria durante una declaración Long al Integer .

También me gustaría saber si esto devuelve lo mismo en todas las versiones de Office. Solo puedo probar en Office 2010 en Windows 7 de 64 bits.

He visto a muchos creer en lo siguiente

VBA convierte todos los valores enteros para escribir Long

De hecho, incluso el artículo de MSDN dice

"En versiones recientes, sin embargo, VBA convierte todos los valores enteros a tipo Long, incluso si se declaran como tipo Integer".

¿Cómo es esto posible? Considere este simple ejemplo.

Sub Sample() Dim I As Integer I = 123456789 End Sub

Si VBA convierte todos los valores de Integer en tipo Long incluso si se declaran como tipo Integer , ¡lo anterior nunca debería dar el error de Overflow !

¿Que me estoy perdiendo aqui? ¿O debería considerar que la declaración es incorrecta y prestar atención a lo que dice el enlace al principio


En lo que respecta a mis pruebas, un entero VBA todavía toma dos bytes (Probado en Access 2016, compilación 8201).

Por lo que puedo encontrar, la conversión implícita a un largo (y viceversa, si es una operación de escritura) ocurre para operaciones , no para almacenamiento . Por ejemplo, si hago myInt + 1 , myInt se lanza a un largo, luego se agrega uno a ese largo, y luego el resultado se devuelve a un int, lo que resulta en una pérdida de rendimiento en comparación con solo usar un Long . Entonces, aunque cuesta menos memoria usar un número entero, todas las operaciones sufrirán un rendimiento.

Como Mathieu Guindon señaló en la respuesta de Enderland / Elysian Fields, probar el almacenamiento de VBA con funciones de VBA no puede probar nada, así que vamos a un nivel más bajo y veamos directamente lo que está almacenado en la memoria, y manipulemos esa memoria.

Primero, declaraciones:

Declare PtrSafe Sub CopyMemory Lib "Kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Long) Public Function ToBits(b As Byte) As String Dim i As Integer For i = 7 To 0 Step -1 ToBits = ToBits & IIf((b And 2 ^ i) = (2 ^ i), 1, 0) Next End Function

Ahora, voy a demostrar dos cosas:

  1. La memoria a la que apunta VarPtr contiene enteros de 16 bits
  2. Manipular esta memoria manipula los enteros que usa VBA, incluso si lo manipula fuera de VBA

El código:

Dim i(0 To 1) As Integer ''Using negatives to prove things aren''t longs, because of the sign bit i(0) = -2 ^ 15 + (2 ^ 0) ''10000000 00000001 i(1) = -2 ^ 15 + (2 ^ 1) ''10000000 00000010 Dim bytes(0 To 3) As Byte CopyMemory VarPtr(bytes(0)), VarPtr(i(0)), 4 Dim l As Long For l = 3 To 0 Step -1 Debug.Print ToBits(bytes(l)) & " "; ''Prints 10000000 00000010 10000000 00000001 Next ''Now, let''s write something back bytes(0) = &HFF ''0xFFFF = signed -1 bytes(1) = &HFF CopyMemory VarPtr(i(0)), VarPtr(bytes(0)), 2 Debug.Print i(0) ''-1

Entonces, podemos estar seguros de que VBA de hecho escribe números enteros de 2 bytes en la memoria y los lee de nuevo desde la memoria, cuando declaramos las cosas como un número entero.


Hasta ahora, no he visto a nadie mencionar el problema de la alineación de bytes. Para manipular los números, debe cargarse en el registro y, como regla, un registro no puede contener más de una variable. Creo que los registros también deben borrarse de la instrucción anterior, por lo que para garantizar que la variable se cargue correctamente, debe realinearse, lo que también puede implicar que el signo se extienda o ponga a cero el registro .

También puede observar la alineación de bytes usando el código VBA:

Public Type x a As Integer b As Integer l As Long End Type Public Type y a As Integer l As Long b As Integer End Type Public Sub test() Dim x As x Dim y As y Debug.Print LenB(x) Debug.Print LenB(x.a), LenB(x.b), LenB(x.l) Debug.Print LenB(y) Debug.Print LenB(y.a), LenB(y.l), LenB(y.b) End Sub

Aunque el UDT x e y contiene el mismo número de miembros y cada miembro tiene el mismo tipo de datos; con la única diferencia en el orden de los miembros, LenB() dará resultados diferentes; En una plataforma de 32 bits, x consume solo 8 bytes, mientras que y necesitará 12 bytes. La palabra alta entre xa y xl y después de xb simplemente se ignora.

El otro punto es que el problema no es exclusivo de VBA. Por ejemplo, C ++ tiene las mismas consideraciones que se ilustran here y here . Entonces, este es en realidad un nivel mucho más bajo y, por lo tanto, no puede "ver" el comportamiento de extensión de signo / extensión de cero al cargar las variables en registros para realizar la operación. Para ver eso, necesitas el desmontaje.


He pasado mucho tiempo trabajando en el entorno de VBA y tengo todas las razones para creer que la afirmación de este artículo es, en el mejor de los casos, engañosa.

Nunca me he encontrado con una situación en la que se realiza una conversión automática inesperada. Por supuesto, la asignación por valor a un tipo más grande (como Double o Long ) estaría implícita.

Un caso específico donde la conversión automática sería un cambio radical sería una asignación a un tipo de Variant . Sin una conversión, el tipo sería VT_I2, con la conversión VT_I4.

Al pasar un tipo entero ByRef a una función que espera un Long emite un tipo no coincidente en Office 2013.

Sospecho que se están refiriendo al almacenamiento interno del número Integer : es muy probable que no estén alineados con palabras de 16 bits en la memoria (cf. un miembro de estructura short en C / C ++). Probablemente estén hablando de eso.


La conversión es solo para la optimización de la memoria, no para el código de usuario. Para el programador, prácticamente no hay cambios ya que los límites mínimo / máximo de los tipos de datos siguen siendo los mismos.

Si toma ese parámetro como un todo, se dará cuenta de que esa declaración es solo en el contexto del rendimiento, y no de otra manera. Esto se debe a que el tamaño predeterminado de los números es Int32 o Int64 (dependiendo de si es un sistema de 32 o 64 bits). El procesador puede procesar hasta ese gran número de una vez. Si declara una unidad más pequeña que esta, el compilador tiene que reducir su tamaño, y eso requiere más esfuerzos que simplemente usar el tipo predeterminado. Y el procesador tampoco tiene ganancia. Entonces, aunque declare su variable como Integer , el compilador le asigna una memoria Long , porque sabe que tiene que hacer más trabajo sin ninguna ganancia.

Como programador de VBA, lo que es importante para usted es: Declare your variables as LONG instead of INTEGER even if you want to store small numbers in them.


Mirando las otras respuestas y la documentación de MSDN, creo que la frase "almacenado internamente" es imprecisa y eso es lo que es confuso.

Tl; DR

Los enteros no se "almacenan internamente" como Longs, es decir, no requieren la misma cantidad de memoria para guardar sus valores que un Long. Por el contrario, se "usan internamente" como Longs, lo que significa que su valor se almacena temporalmente en una variable Long cada vez que se accede (por ejemplo, incrementando un contador de bucle) antes de ser copiado, y en general, una matriz de Integers requerirá la mitad de la memoria como una serie de Longs.

La respuesta de @enderland muestra que el diseño de memoria de enteros, matrices de enteros y UDT compuestos por enteros como DWORD se ajusta a la idea de que el valor contenido en una variable declarada como un entero ocupa 2 bytes de memoria.

Esto es desde el punto de vista del código VBA, lo que significa que es posible suponer que

  1. Las ubicaciones y tamaños de memoria proporcionados por VarPtr y LenB respectivamente son incorrectos (mentiras) en el caso de Integers para evitar romper el código existente cuando se realizó el cambio de los sistemas de 16 a 32 bits.
  2. Hay algún tipo de capa de abstracción que significa que la memoria aparece como una cosa pero en realidad es otra

Podemos descartar estos dos.

Es posible utilizar la API CopyMemory con una dirección dada por VarPtr y un ancho dado por LenB para sobrescribir valores en una matriz directamente. La API no está bajo el control de VBA, y todo lo que hace es escribir bits directamente en la memoria. El hecho de que esto sea posible significa que VarPtr debe apuntar a un área en la memoria donde se LenB bytes LenB para almacenar el valor de ese entero; de otra manera, 2 bytes es la cantidad de espacio utilizado para codificar el valor de un entero.

Sin embargo, la capa de abstracción aún podría ser cierta; VBA podría contener una matriz de memoria espaciada de 2 bytes (SAFEARRAYS son todas memorias consecutivas, es por eso que CopyMemory puede escribir 2 entradas a la vez) a donde apunta VarPtr. Mientras tanto, un bloque separado de memoria de 4 bytes espacia la sombra del bloque de 2 bytes, manteniéndose constantemente sincronizado para que los números enteros se puedan almacenar como Longs. Suena raro pero podría pasar ¿verdad?

No lo hace, y podemos ver esto mirando la memoria del proceso en el Administrador de tareas:

Inactivo, Excel usa 155,860KB de memoria (155,860 * 1024 bytes)

Ejecuta esto:

Sub testLongs() Dim longs(500, 500, 500) As Long Stop End Sub

... y 647,288KB . Tomar la diferencia y dividir por el número de elementos de la matriz da ~ 4.03 bytes por Long . La misma prueba para enteros:

Sub t() Dim ints(500, 500, 500) As Integer Stop End Sub

... da 401,548 , o ~ 2.01 bytes por entero

Habrá una ligera variación en el uso de la memoria inactiva, por lo que los números exactos no importan, pero claramente la matriz Integer está utilizando ~ la mitad de la memoria de la matriz Long

Entonces, mi interpretación del artículo de MSDN es la siguiente:

En cuanto a la memoria, los enteros se almacenan realmente como valores de 2 bytes, no como longitudes de 4 bytes. No hay abstracción o truco con punteros para ocultar esto de nosotros.

Más bien, el artículo nos dice que cuando se usan números enteros en operaciones (multiplicación / suma, etc.) sus valores se copian a la mitad inferior de un int32 / VBA Long , el cálculo se realiza de una manera optimizada y amigable de 32 bits, y luego el resultado se copia de nuevo a Integer y se generan errores de desbordamiento según sea necesario. Para Longs no hay necesidad de copiar hacia adelante y hacia atrás (de ahí la recomendación).


Un número entero declarado como un número Integer todavía se marca como un número Integer . La documentación de msdn hace referencia a cómo se almacena internamente la variable. En un sistema de 32 bits, un entero se almacenará en 32 BITS no en bytes , mientras que en un sistema de 16 bits el valor se almacena en un espacio o registro de 16 BIT , se habría almacenado en 16. De ahí el tamaño máximo.

No hay conversión de tipo en lo que respecta a VBA. Un int es un int y un long es un long, a pesar de que ahora ocupan la misma cantidad de espacio .