Looking at the other answers and the MSDN documentation, I think the phrase "Internally stored" is imprecise and that's what's confusing.
Tl;DR
Integers aren't "stored internally" as Longs, that is they do not require the same amount of memory to save their values as a Long does. Rather they are "used internally" as Longs, meaning their value is temporarily stored in a Long variable whenever it is accessed (e.g incrementing a loop counter) before being copied back, and generally speaking, an array of Integers will require half as much memory as an array of Longs.
@enderland's answer shows that the memory layout of Integers, Integer Arrays and UDTs composed of Integers such as a DWORD all conform to the idea that the value contained in a variable declared as an integer takes up 2 bytes of memory.
This is from the viewpoint of VBA code, meaning it's possible to assume that
- The memory locations and sizes given by
VarPtr
and LenB
respectively are incorrect (lies) in the case of Integers to avoid breaking existing code when the switch from 16 to 32 bit systems took place
- There is some sort of abstraction layer which means the memory appears as one thing but is actually another
We can rule these both out.
It is possible to use the CopyMemory API with an address given by the VarPtr and a width given by LenB to overwrite values in an array directly. The API is not under VBA's control, and all it does is directly write bits to memory. The fact that this is possible at all means that VarPtr
must point to an area in memory where LenB
bytes are used to store the value of that Integer; no other way around it, 2 bytes is the amount of space used to encode an Integer's value.
The abstraction layer could still be true though; VBA could hold one array of 2 byte spaced memory (SAFEARRAYS are all consecutive memory, that's why CopyMemory can write 2 entries at once) where VarPtr points to. Meanwhile a separate 4 byte spaced block of memory shadows the 2 byte spaced block, staying constantly in sync so that Integers can be stored as Longs. Sounds weird but could happen right?
It doesn't, and we can see this by looking at the process memory in Task Manager:
Idle, Excel uses 155,860KB
of memory (155,860 * 1024 bytes)
Run this:
Sub testLongs()
Dim longs(500, 500, 500) As Long
Stop
End Sub
...and it spikes to 647,288KB
. Taking the difference and dividing by the number of array elements gives ~4.03 bytes per Long. The same test for Integers:
Sub t()
Dim ints(500, 500, 500) As Integer
Stop
End Sub
...gives 401,548KB
, or ~2.01 bytes per Integer
There will be a slight variation in the idle memory usage so the exact numbers don't matter, but clearly the Integer array is using ~ half the memory of the Long array
So my interpretation of the MSDN article is the following:
Memory-wise, Integers are really stored as 2 byte values, not as 4 byte Longs. There is no abstraction or trickery with pointers to hide this from us.
Rather the article tells us that when Integers are used in operations (multiplication/addition etc.) their values are fist copied to the lower half of an int32
/ VBA Long
, the calculation takes place in an optimized 32-bit friendly way, and then the result is copied back to Integer and overflow errors are raised as necessary. For Longs there is no need to copy forward and back (hence the recommendation).