qb64
qb64 copied to clipboard
`REDIM _PRESERVE` multi-dimensional array shifts data
Issue by Kroc
Monday Jan 22, 2018 at 21:49 GMT
Originally opened as https://github.com/Galleondragon/qb64/issues/27
Though the unpredictability of changing the sizes of non-last dimensions in a multi-dimension arrays is common knowledge in most forms of BASIC, the QB64 documentation does not specifically call this out. The documentation implies that it should work, and the compiler / run-time gives no warnings or error. Your data just magically gets messed up!
Test code:
PRINT "REDIM TEST"
REDIM TEST(1 TO 2, 1 TO 3) AS INTEGER
LET TEST(1, 1) = 100
LET TEST(1, 2) = 200
LET TEST(1, 3) = 300
PRINT TEST(1, 1); TEST(1, 2); TEST(1, 3)
REDIM _PRESERVE TEST(1 TO 3, 1 TO 3) AS INTEGER
PRINT TEST(1, 1); TEST(1, 2); TEST(1, 3)
END
Suggested solutions:
- Changing the bounds of a non-last dimension in a multi-dimensional array should produce a run-time error
- QB64 should ensure data integrity and move the data to the correct locations
I am building a two dimensional cross-check array, where the bounds are increasing as new data is read in. It would be difficult to store all the data (without knowing the final bounds until read) and dimension the array at the end.
Comment by ghost
Monday Jan 22, 2018 at 23:59 GMT
Verified.
QB64 indeed gets this wrong somehow, and I'm not entirely certain why INTEGER
is taking up 4 bytes instead of 2 originally, or why it switches to require 6 bytes instead of the original 4, though it at least sets the correct segment when it comes to usage with VARSEG
:
Before:
100 200 300 // Value
7C31B0 7C31B4 7C31B8 // real offset (_MEM)
9FFF:0 9FFF:4 9FFF:8 // VARSEG:VARPTR
After:
100 0 0 // Value
7C31A0 7C31A6 7C31AC // real offset (_MEM)
9FFE:0 9FFE:6 9FFE:C // VARSEG:VARPTR
The proper output would be something like the following:
Before:
100 200 300 // Value
7C31B0 7C31B2 7C31B4 // real offset (_MEM)
9FFF:0 9FFF:2 9FFF:4 // VARSEG:VARPTR
After:
100 200 300 // Value
7C31A0 7C31A2 7C31A4 // real offset (_MEM)
9FFE:0 9FFE:2 9FFE:4 // VARSEG:VARPTR
The same bug occurs even if allocated outside of the "cmem" block:
Before:
100 200 300
000B20 000B24 000B28
After:
100 0 0
000B20 000B26 000B2C
Expected output, assuming no reallocation:
Before:
100 200 300
000B20 000B22 000B24
After:
100 200 300
000B20 000B22 000B24
this is not an actual bug, but a mismatch between
QB64's Little-Endian array logic
address = leftDimensionAddress + (rightDimensionAddress * leftDimensionSize)
and the Big-Endian array logic of the previous posters.
address = rightDimensionAddress + (leftDimensionAddress * rightDimensionSize)
edit: the fact that the addresses were shifting by 4 bytes (2 integers) and 6 bytes (3 integers) respectively should have clued them in on this.