Unexpected structure behaviour

Here you can link to screenshots and demos etc. of programs made using BBC BASIC
tpegc
Posts: 5
Joined: Tue 24 Apr 2018, 16:28

Unexpected structure behaviour

Post by tpegc »

Hello - apologies if this is the wrong place, or badly posted - newbie alert!

First off thank you to Richard Russell for creating and maintaining BBC Basic for Windows and SDL. I went from ZX80 Basic to BBC Micro Basic to university FORTRAN and then into a plethora of languages across my career, and it is with great joy that I return to BBC Basic for pure fun now.

I have been playing with structures and found some unexpected behaviour. The following code should only change one array structure per dump, but it modifies others where indicated. It is entirely possible that I am being stupid - if so apologies again - but any explanation would be very welcome

Code: Select all

      MODE 14

      DIM a{t,s$}
      DIM array{(4)}=a{}
      FORi=0TO4
        array{(i)}.t=-1
        array{(i)}.s$="null"
      NEXT
      PROCdump

      PROCset(a{},0,"Zero?")
      array{(0)}=a{}
      PROCdump

      PROCset(a{},1,"11")
      array{(1)}=a{}
      PROCdump

      PROCset(a{},2,"222")
      array{(2)}=a{} : REM unexpected, array{(1)}.s$="22"
      PROCdump

      PROCset(a{},3,"3333")
      array{(3)}=a{} : REM unexpected, array{(0)}.s$="3333?"
      PROCdump

      array{(4)}.t=4.4
      array{(4)}.s$="Four.four"
      PROCdump

      a{}.t=4
      a{}.s$="44444"
      array{(4)}=a{} : REM unexpected, array{(3)}.s$="4444"
      PROCdump

      END

      DEFPROCdump:LOCALi
      FORi=0TO4
        PRINT"dataitem ";i;", t=";array{(i)}.t;" s$=""";array{(i)}.s$;""""
      NEXT
      PRINT
      ENDPROC

      DEFPROCset(RETURN struc{},p,q$)
      struc{}.t=p
      struc{}.s$=q$
      ENDPROC
tpegc
Posts: 5
Joined: Tue 24 Apr 2018, 16:28

Re: Unexpected structure behaviour

Post by tpegc »

I definitely should have said - this is BBC Basic for SDL 2.0, with identical behavior in BB4W v6.16a
Richard Russell
Posts: 706
Joined: Tue 18 Jun 2024, 09:32

Re: Unexpected structure behaviour

Post by Richard Russell »

tpegc wrote: Wed 15 Jul 2026, 13:45 I have been playing with structures and found some unexpected behaviour.

Code: Select all

      array{(0)}=a{}
From the manual "Do not copy structures containing string members; since only the string descriptor is copied, not the string itself, you are likely to confuse BASIC and may even crash it".

Since you are doing something specifically forbidden, the behaviour may be 'surprising' but it's not "unexpected"! :lol:
tpegc
Posts: 5
Joined: Tue 24 Apr 2018, 16:28

Re: Unexpected structure behaviour

Post by tpegc »

:oops: I really should have read the entire section on structures, not just the first bit and then assumed they would work how I wanted them to!

Thank you for the super quick and, in the circumstances, very gentle correction of my stupidity :D
Richard Russell
Posts: 706
Joined: Tue 18 Jun 2024, 09:32

Re: Unexpected structure behaviour

Post by Richard Russell »

tpegc wrote: Thu 16 Jul 2026, 10:05 Thank you for the super quick and, in the circumstances, very gentle correction of my stupidity :D
Absolutely not "stupidity". Indeed assuming that something will work in the way that it looks as if it ought to work - and furthermore runs without reporting an error - is entirely reasonable.

It's largely for implementation convenience, as well as speed, that all the 'vector copy' operations simply check that the types and sizes are compatible, then do a straight memcpy-style copy from source to destination. This works fine for everything except structures containing 'moveable' strings.

As for workarounds, there are two main approaches that you could consider. Firstly, use 'fixed' (NUL-terminated) strings rather than 'moveable' strings in the structure (here with a maximum length of 255 bytes):

Code: Select all

      MODE 20

      DIM a{t,s&(255)}
      DIM array{(4)}=a{}
      FORi=0TO4
        array{(i)}.t=-1
        array{(i)}.s&()="null"
      NEXT
      PROCdump

      PROCset(a{},0,"Zero?")
      array{(0)}=a{}
      PROCdump

      PROCset(a{},1,"11")
      array{(1)}=a{}
      PROCdump

      PROCset(a{},2,"222")
      array{(2)}=a{}
      PROCdump

      PROCset(a{},3,"3333")
      array{(3)}=a{}
      PROCdump

      array{(4)}.t=4.4
      array{(4)}.s&()="Four.four"
      PROCdump

      a{}.t=4
      a{}.s&()="44444"
      array{(4)}=a{} 
      PROCdump

      END

      DEFPROCdump:LOCALi
      FORi=0TO4
        PRINT"dataitem ";i;", t=";array{(i)}.t;" s&()=""";array{(i)}.s&();""""
      NEXT
      PRINT
      ENDPROC

      DEFPROCset(RETURN struc{},p,q$)
      struc{}.t=p
      struc{}.s&()=q$
      ENDPROC
Secondly, write a 'structure copy' procedure that actually does the right thing:

Code: Select all

      MODE 20

      DIM a{t,s$}
      DIM array{(4)}=a{}
      FORi=0TO4
        array{(i)}.t=-1
        array{(i)}.s$="null"
      NEXT
      PROCdump

      PROCset(a{},0,"Zero?")
      PROCcopy(array{(0)},a{})
      PROCdump

      PROCset(a{},1,"11")
      PROCcopy(array{(1)},a{})
      PROCdump

      PROCset(a{},2,"222")
      PROCcopy(array{(2)},a{})
      PROCdump

      PROCset(a{},3,"3333")
      PROCcopy(array{(3)},a{})
      PROCdump

      array{(4)}.t=4.4
      array{(4)}.s$="Four.four"
      PROCdump

      a{}.t=4
      a{}.s$="44444"
      PROCcopy(array{(4)},a{})
      PROCdump

      END

      DEFPROCdump:LOCALi
      FORi=0TO4
        PRINT"dataitem ";i;", t=";array{(i)}.t;" s$=""";array{(i)}.s$;""""
      NEXT
      PRINT
      ENDPROC

      DEFPROCset(struc{},p,q$)
      struc{}.t=p
      struc{}.s$=q$
      ENDPROC

      DEF PROCcopy(dst{},src{})
      dst.s$=""
      dst{}=src{}
      ]^dst.s$=0
      dst.s$=src.s$
      ENDPROC
tpegc
Posts: 5
Joined: Tue 24 Apr 2018, 16:28

Re: Unexpected structure behaviour

Post by tpegc »

Thank you again!
First for (silently) correcting my mistake about passing structures, second for the two examples. The first 'fixed' string one I understand, but I'm missing something with the second one. I had already tried something like this (I've swapped to your names and dropped my spurious RETURN :) ):

Code: Select all

      DEF PROCcopy(dst{},src{})
      dst{}.t=src{}.t
      dst.s$=src.s$
      ENDPROC
which appears to work (I'm happy to be corrected). I can see that would get unwieldy for several structure elements, so dst{}=src{} has the advantage there, but I don't fully understand the extra .s$ processing in your example. Taking it line by line:

dst.s$="" --- I don't know why this is needed... unless it's in case dst.s$ is large and this would free that space up which will otherwise be lost in the next steps.

dst{}=src{} --- ]^dst.s$ now matches ]^src.s$, i.e. the same memory is used, beware this may end in tears.

]^dst.s$=0 --- presumably puts dst.s$ into a state such that the next line...

dst.s$=src.s$ --- makes a new data area copy of src.s$ (regardless of the size of src.s$) and ]^dst.s$ gives its address and length and all is now good.

Ah well now assuming that is correct, I think working my way through it in order to ask a question has answered the question.
Richard Russell
Posts: 706
Joined: Tue 18 Jun 2024, 09:32

Re: Unexpected structure behaviour

Post by Richard Russell »

tpegc wrote: Fri 17 Jul 2026, 15:25 I can see that would get unwieldy for several structure elements
Exactly, and slow. My PROCcopy will copy a structure of any size and complexity so long as it has just one moveable string member s$. As such it's more general-purpose.
dst.s$="" --- I don't know why this is needed... unless it's in case dst.s$ is large and this would free that space up
Not only if dst.s$ is "large". Even if it's small, it's a memory leak which can accumulate and eventually use up all memory. There's no particular reason to suppose that PROCcopy will only be called a few times, it might be called millions of times. Leaving a memory leak unfixed is very bad practice!
]^dst.s$=0 --- presumably puts dst.s$ into a state such that the next line...
dst.s$=src.s$ --- makes a new data area copy of src.s$
Yes. It's a little unwieldy to say "puts dst.s$ into a state...", it just zeroes it, restoring it to how it was before the structure copy.
tpegc
Posts: 5
Joined: Tue 24 Apr 2018, 16:28

Re: Unexpected structure behaviour

Post by tpegc »

Thank you again. I need to check one program that uses a string in a structure that has so far worked without problems (I think the structure copies are always using a "" null string, and string assignments made directly to the string element, so "all" I have is a memory leak (yes less than ideal, but if this were for the embedded systems I used to work with, I wouldn't be permitted dynamic memory allocation at all - which is a constraint I'm enjoying not having :lol: )).
Plus I now see where my initialising strings to their maximum foreseen length (based on the behaviour of the original BBC Micro BASIC) has been... unhelpful. Time to ditch my assumptions of 40+years ago and read the current manual.
Thank you again for giving me back the joy of BBC Basic - and this time around I don't need to sit typing cross-legged on the floor in front of the only TV in the house (a DSE assessment nightmare), or listening to the 1200 baud burble of the cassette player :D
Richard Russell
Posts: 706
Joined: Tue 18 Jun 2024, 09:32

Re: Unexpected structure behaviour

Post by Richard Russell »

tpegc wrote: Sat 18 Jul 2026, 12:16 "all" I have is a memory leak...
Arguably, a bug which goes completely unnoticed - until the program is eventually deployed and crashes for no obvious reason after running for a few days or months - is the worst possible kind! :shock:
Plus I now see where my initialising strings to their maximum foreseen length (based on the behaviour of the original BBC Micro BASIC) has been... unhelpful.
That's not been desirable or necessary since BBC BASIC version 5 in 1986, so only forty years. :roll:

But to be fair it's not likely to do any significant harm either.