5

I'm trying to implement the CHANGE command from Dartmouth BASIC v4 and later. It allows you to convert a string into an array of numbers and back again. One key bit of the concept is that the length of the string is put into index zero in the array. So for instance, this code (adapted from the v4 manual page 67):

10 DIM A(50)
20 A$="HELLO"
30 CHANGE A$ TO A
40 FOR I=0 TO A(0)
50 PRINT A(I),
60 NEXT I

Works by looping from 0 to the string length.

But... Dartmouth BASIC also generally starts with array index 1, like most BASICs. So the two attempts to access A(0) and A(I) when I=0 should both give an array-out-of-bounds error. Normally you should have to use OPTION BASE 0 to do this, which this code does not, and I can't find that option in the v4 manual anywhere. Moreover, other examples looping over arrays invariably start at 1, for instance, page 57, while others suggest it starts at 0, like page 21.

All the online emulators emulate earlier versions only, so I have no easy way to test this.

Anyone know what's going on here?

6
  • The last sentence of the last paragraph of the 4th edition section of the article at Wikipedia is pretty useless, huh? Between that and the example in the 4th ed manual it almost makes you think this might have been special cased in some way ...
    – davidbak
    Aug 18, 2022 at 1:34
  • Does "[...] while every vector has a component 0, and every matrix has a row 0 and a column 0, [...]" on page 54 give a sign that an index of zero is allowed? Aug 18, 2022 at 7:49
  • 3
    I am quite sure that index 0 was quite common in other dialects of BASICs. Microsoft Basic-80 (or MBASIC) and BBC BASIC are two well known that made the index 0..n for DIM X(n)
    – UncleBod
    Aug 18, 2022 at 7:56
  • I used BASIC on the Open University timesharing system, which I think ran some HP system, and it had arrays starting at index 0. Though for most uses, you could ignore it. As in Spinal Tap, this meant the default size of arrays went to 11.
    – dave
    Aug 18, 2022 at 11:41
  • 1
    BTW you should iterate FOR I=1 TO A(0) as you do not want to print the length. Aug 18, 2022 at 13:06

2 Answers 2

8

Version 2 of Dartmouth BASIC changed the dimensions of vectors (arrays) from one-based to zero-based. This was kept from there on and inherited by most other BASIC, some later offering an OPTION BASE to select what's preffered.


The Dartmouth BASIC V4 describes this (indirect) with the DIM command in section 1.7.8 on p. 37:

1.7.8 DIM

Whenever we want to enter a list or a table with a subscript greater than 10, we must use a DIM statement to inform the computer to save us sufficient room for the list or the table.

Examples:   20 DIM H(35)
           35 DIM Q(5,25)

The first would enable us to enter a list of 35 items (or 36 if we use H(0)), and the latter a table 5 x 25, or by using row 0 and column 0 we get a 6 x 26 table.

Highlighting is mine (*1).

The same way Section 1.4, Lists and Tables (as they call arrays) starting on page 20, implicit talks about elements of numbered as 0..n. For example on p.23 a sample program without using DIM is explained as:

This sample program did not need a dimension statement, since the computer automatically saves enough space to allow all subscripts to run from 0 to 10.

This makes it pretty clear that the default base for indices is zero.

This is further enhanced by the way MATrix operations are described in Section 2.6. On page 54 the V4 manual states:

... However, the user has to be careful to keep (and to understand!) the conventions "built into" the language. We will discuss, below, the individual MAT statements.

The following convention has been adopted for MAT: while every vector has a component 0, and every matrix has a row 0 and a column 0, the MAT instructions ignore these. Thus if in a MAT instruction we have a matrix of dimension M-by-N, the rows are numbered 1, 2,...,M, and the columns 1, 2,...,N.

At that point it might be helpful to keep in mind that the MAT instructions were originally (in V2) a set of external subroutines, following their own logic.


This isn't any new behaviour, as V2 states on p.39

BASIC provides that each list has a subscript running from 0 to 10, inclusive. Each subscript in a table may run from 0 to 10. If the user desires to have larger lists or tables, he may use a DIM statement in his program. For example,

        10 DIM A(17)

indicates to the computer that the subscript of the list A runs from 0 to 17, inclusive; ...


But... Dartmouth BASIC also generally starts with array index 1,

Only true for the initial Version. Already the second one (CARDBASIC) changed that to zero-based.

like most BASICs.

'most' is quite debatable, as for example all MS default to zero, which is a quite large user base. Same goes for independent microprocessor developments like BBC-BASIC, Sinclair BASIC, Locomotive BASIC (*2) or even way before that for Olivetti P6060 BASIC.

Normally you should have to use OPTION BASE 0 to do this, which this code does not, and I can't find that option in the v4 manual anywhere.

OPTION BASE is a way later addition

Moreover, other examples looping over arrays invariably start at 1, for instance, page 57, while others suggest it starts at 0, like page 21.

Examples are to show some point, the one on p.57 is about MATrix operations, which inherently started at one (*3,4).

All the online emulators emulate earlier versions only, so I have no easy way to test this.

All versions except the initial should have arrays starting at zero. So any emulation of CARDBASIC or later should show exactly this behaviour.


*1 - The last part is especially interesting, as they seem to call the 0th element explicit 'row and column' as if they are intended for labling or summation.

*2 - I find int again and again impressive that the English computer industry came up with so many different - and successful - BASIC implementations.

*3 - Since both, zero-based indices and MAT statements, were introduced with CARDBASIC, I can't help to see index 0 for either dimension als intended for label or summation purpose (see *1).

*4 - Beloved Atari-BASIC shows a similar but even more challenging behaviour. Here DIMensioning a numerical arrays have its first element at index 0, but string-"arrays" at 1. So while in Dartmouth V4 the 0th element of a matrix can be addressed, doing so with a string in Atari BASIC will result in a bad subscript :))

6
  • 1
    For most purposes, the only effect of having arrays start at zero rather than one would be the allocation of some extra storage to hold an extra element (or row, column, etc.) Having most arrays start at zero would make an implementation compatible with most programs that expect them to start at zero or programs that expect them to start at 1. On the other hand, after DIM A$(5):A$="HELLO", code that expects strings to start at 1 would expect A$(1) to be H, while code that expects them to start at zero would expect it to be E. Having strings start at 0 would thus break a lot of code.
    – supercat
    Aug 18, 2022 at 15:46
  • 1
    Re: "compute*-" and "row and column" - I think you may have read a version with typos or OCR errors. It says "computer" and "row 0 and column 0" in this one: bitsavers.org/pdf/dartmouth/BASIC_4th_Edition_Jan68.pdf
    – Bavi_H
    Aug 19, 2022 at 13:23
  • @supercat - but a string is not an array of characters in proper (Dartmouth) BASIC. That's why the CHANGE statement exists. DIM A$(5) gets you an array of 6 strings, not a single string of length 5 (or 6).
    – dave
    Aug 19, 2022 at 15:24
  • @another-dave: I'm pretty sure that in HP-2000 BASIC, which was based on an earlier version of Dartmouth BASIC, DIM A$(11) would specify that A$ is a single string that can hold a maximum of eleven characters, and if A$ was set to GRASSHOPPER, the expression A$(5,8) would yield SHOP. Later versions of Dartmouth BASIC did things totally differently, but I think the way HP-2000 BASIC and Atari BASIC did things were both faithful to the earlier Dartmouth dialects.
    – supercat
    Aug 19, 2022 at 15:36
  • 1
    @supercat : Maybe so -- I think I used HP-2000 BASIC on the UK's Open University system (my maths teacher was taking a Computer Science degree with the OU), but I can't recall the details. The oldest Dartmouth manual for BASIC 'with strings' at bitsavers is for the 4th edition and it states DIM A$() gets an array of string (p64).
    – dave
    Aug 19, 2022 at 16:44
1

In retrospect the answer is "it doesn't matter".

The primary goal of the program is to run known-good programs. As such, we can assume that accesses to A(0) only occur in places where that would be legal.

So it's safe to add a 0th entry to all arrays, as those programs assuming 1-base system would never access it anyway. This will lead to some situations where an illegal access will be allowed, like:

100 INPUT"what index to print";A
110 PRINT B(A)

when the user enters 0, but this seems non-fatal.

9
  • It's non-fatal as long as the default value of that element is well-defined. In C, where array access de-sugars (roughly) to pointer arithmetic, and declaring a variable doesn't imply any initialisation, failing to validate bounds is a serious problem.
    – IMSoP
    Aug 18, 2022 at 15:18
  • @IMSoP: On many historical C implementations, an out-of-bounds array read would never have any side effect beyond yielding a possibly meaningless value, and on many more it could only have side effects if it was sufficiently out of bounds as to reach read-triggered memory/mapped I/O. Further, on systems with protected memory, the only possible side effect beyond yielding a meaningless value would be an abnormal program termination--not elegant, but not as disastrous as the arbitrary memory corruption that can result with today's optimizing compilers.
    – supercat
    Aug 18, 2022 at 15:48
  • @supercat I never said anything about having side effects, I said that reading out of bounds can be a serious problem - if the "meaningless value" is actually some other variable in the process, tricking a routine into returning it can reveal privileged information to an attacker.
    – IMSoP
    Aug 18, 2022 at 16:25
  • 1
    True, the addition of a zero index is upward compatible to all programs assuming that indices start at 1. I guess that's why they added it. THat is, at least as long as your BASIC don't contain modern constructs like FOR EACH I in B() ... or FOR I = B().FIRST to B().LAST, as that would assign 0 to I during the first iteration.
    – Raffzahn
    Aug 18, 2022 at 16:45
  • 1
    @supercat I'm really not here to discuss the pros and cons of language design choices. I was adding a specific caveat to the statement in the answer that allowing out of bounds access is no big deal: it's no big deal as long as that access doesn't expose uninitialised memory. Presumably, the relevant versions of BASIC made such a guarantee, but it's an unstated assumption in the answer which I thought might be useful to add.
    – IMSoP
    Aug 18, 2022 at 17:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .