News for hackers. Stuff that matters.
Re:Why do arrays start at 0? by bluetomcat on Wednesday August 24, 2022 @03:34PM (#32584621)
The "language" that has a 0-based indexing scheme is assembly. An HLL with 1-based counting that compiles to assembly will introduce additional computational overhead for the translation of the index.

If 1-based indexing was used in assembly, then "mov 1(%ebx),%eax" would be the equivalent of "mov (%ebx),%eax".

Re:Why do arrays start at 0? by jiveturkey on Wednesday August 24, 2022 @04:22PM (#32585247)
> additional computational overhead

at compile time, not runtime.

Re:Why do arrays start at 0? by saalweachter on Wednesday August 24, 2022 @04:25PM (#32585289)
For constant addressing; for arr[i] = 2, you'll still need to subtract 1 from i with 1-based addressing when converting to machine instructions.
Re:Why do arrays start at 0? by nousermane on Wednesday August 24, 2022 @08:19PM (#32587700)
> for arr[i] = 2, you'll still need to subtract 1

You can, but don't need to. Just have compiler store array pointer constant as (arr-1) instead of arr, et voila, zero runtime overhead.

Or, for modern(-ish) ISAs, often you can add/substract a small constant at runtime, with no extra cycles taken. For example, for x86_64:

  # rbp contains "true" pointer to arr
  # rax contains 1-based array index
  mov rax, QWORD PTR [rbp-8+rax*8]
Re:Why do arrays start at 0? by astrobe_ on Thursday August 25, 2022 @04:42AM (#32591180)
Another option for users is to "sacrifice" the 0th entry of an array. Depending on the size(as in sizeof) of the entry, it can be worth it.

A benefit is that you can then use 0 as a sentinel value; for instance if you have a find() routine that surely can fail, it can just return 0 instead of having e.g. -1 (which can introduce minor issues).

In my experience, though, I am so used to 0-based index that switching schemes can cause stupid off-by-one bugs. I guess that's the main reason behind complains about Lua. It's not that "natural" arrays are thought of as bad, but mixing both schemes (often C an Lua) is error-prone.

Re:Why do arrays start at 0? by murderfs on Thursday August 25, 2022 @02:04AM (#32590137)
It's basically only x86 among modern ISAs that lets you do base + literal + register * literal, aarch64 only gives you base + register shifted by a literal, and I believe RISC-V is similar: https://gcc.godbolt.org/z/dz768768z
Re:Why do arrays start at 0? by snvzz on Thursday August 25, 2022 @03:22AM (#32590655)
>and I believe RISC-V is similar

Yes it is. It was evaluated, carefully weighted and discarded, as it was not worth it.

Re:Why do arrays start at 0? by kaba0 on Thursday August 25, 2022 @03:42AM (#32590792)
But it will still execute with likely no extra time at all due to OOE and how fast arithmetics are.
Re:Why do arrays start at 0? by murderfs on Thursday August 25, 2022 @01:56PM (#32597436)
OOE doesn't necessarily save you if you end up with a hard dependency on the value of the read (and even if it did, the little cores on ARM SoCs are in-order). This is a pretty obvious candidate for macro-op fusion, but I'm not sure whether this actually happens (and if it happens on ARM little cores, etc.)
Re:Why do arrays start at 0? by NohatCoder on Thursday August 25, 2022 @06:16AM (#32591756)
If it is not on the hot path, it is likely free, but not guaranteed. If it is on the hot path then it is wasting a whole cycle. And of course in highly ALU-dependent code it is another instruction, so a fraction of a clock.
Re:Why do arrays start at 0? by kaba0 on Thursday August 25, 2022 @08:23AM (#32592744)
What do you mean it wastes a whole cycle? It may indeed have worse performance due to blowing the instruction cache, but I don’t see why would out-of-order execution be slower on the hot path - I doubt there would be too many hot paths without any dependence on memory fetches outside specific benchmarks - the memory loads will take significantly more time even if they hit cache.
Re:Why do arrays start at 0? by shpx on Wednesday August 24, 2022 @09:02PM (#32588026)
What if the variable is used for array indexing and something else, like displayed to the user?
Re:Why do arrays start at 0? by vukgr on Wednesday August 24, 2022 @04:30PM (#32585337)
Can't say I've really thought this through, but couldn't you just subtract 1 (*sizeof(X)) from the arr address?
Re:Why do arrays start at 0? by tremon on Wednesday August 24, 2022 @06:17PM (#32586535)
What would memset(arr, 0, sizeof(arr)) do in that case? I can see myriad problems with having arr itself point outside the memory range allocated to arr[min..max].
Re:Why do arrays start at 0? by saalweachter on Wednesday August 24, 2022 @04:45PM (#32585503)
Hmm, maybe?

You'd still have the odd subtract here or there, but for many use patterns you could probably ignore the overhead.

Re:Why do arrays start at 0? by joaonmatos on Wednesday August 24, 2022 @09:38PM (#32588317)
For most cases you could get away with some index transformation logic in the compiler (but probably your language would need special types for indices, so I do agree with you that it is not worth the trouble).
Re:Why do arrays start at 0? by lucas_membrane on Thursday August 25, 2022 @03:18AM (#32590626)
> An HLL with 1-based counting that compiles to assembly will introduce additional computational overhead for the translation of the index

Not necessarily so for translation of an index that is evaluated at run time. PL1 (back in the 1960's) compiled arrays into 'dope vectors'. Each array's dope vector included highest and lowest valid subscripts for each dimension of the array and the address where the element with all zero subscripts would be found, if it existed.

Re:Why do arrays start at 0? by unilynx on Wednesday August 24, 2022 @04:43PM (#32585478)
Not sure if assembly/machine code is that relevant. If one based indexing was more prevalent, the LEA instruction on x86 would just subtract one during execution
Re:Why do arrays start at 0? by cryptonector on Wednesday August 24, 2022 @05:40PM (#32586137)
u/blutomcat's point clearly (I think) was that instruction sets didn't do what you suggest, so zero-based index is (was and still is) what you'd do if you wrote in assembly (which was not uncommon!), and on any given platform, assembly was the main language 50-60 years ago. It follows that it's easier to carry that over to higher level programming languages.

Whether that's what actually happened in the cases of HLLs that adopted zero-based arrays or not, I don't know. But today, to me, zero-based array indexing feels very natural, and the idea that zero-based arrays being simpler in assembly carrying over to HLLs seems at the very least plausible.

Re:Why do arrays start at 0? by dboreham on Wednesday August 24, 2022 @06:04PM (#32586390)
"just subtract one" would take a long time 50-60 years ago.
Re:Why do arrays start at 0? by Jtsummers on Wednesday August 24, 2022 @06:15PM (#32586511)
If you did this, you'd subtract one once at array creation. Or subtract whatever the offset is (really, you'd subtract 1 * data type size). Under the hood, using C syntax instead of assembly:

  int foo[n]; // what the user wrote

  // what happens behind the scenes, after a fashion
  int* foo = malloc(n * sizeof(int); // or sp - n * sizeof(int) if stack allocated; subtraction since stacks usually grow "down"
  foo = foo - 1;
All references into foo will now work just fine so long as they are within the [1,n] range (same issues as with 0-based there since C doesn't carry size information for checking array bounds access). This adds one extra instruction per allocation (which includes allocation on the stack) for all non-0 offsets, but then all access will have the same cost whether 0-based, 1-based, or arbitrary-based. That's a non-zero cost, but it's not exorbitant since you'll be accessing much more often than allocating (and if it's reversed, something weird is happening).
Re:Why do arrays start at 0? by maxdamantus on Wednesday August 24, 2022 @08:19PM (#32587694)
Since as you say, C doesn't carry size information, you would actually need to do it each time a pointer is created from an object. In C you can have arrays of arrays (not pointers) or arrays in structs, where there is no pointer on creation:

  // 11 arrays are created here
  int foo[10][10];
It doesn't make sense to create pointers for the 10 inner arrays, so the subtraction would presumably happen when referring to each inner array:

  int *z = foo[x]; // (char *)foo_sub_1 + (sizeof *foo)*x - (sizeof **foo)
  foo[x][y]; // (char *)foo_sub_1 + (sizeof *foo)*x - (sizeof **foo) + (sizeof **foo)*y
In any case, this is more work, both for the computer and for the human. It's analogous to trying to figure out "which year of which century is X in?" (2022 is the 22nd year of the 21st century):

  year(x) = (x - 1)%100 + 1
  century(x) = floor((x - 1)/100) + 1
The above formulae work for the 1-based convention, because they do the necessary 1 subtraction/addition in the right places. If we instead counted from 0, things would be a lot simpler (2021 would be year 21 of century 20):

  year(x) = x%100
  century(x) = floor(x/100)
Unfortunately, the Romans started counting years before anyone knew about 0. I think this is basically why we have a convention of counting from 1: 0 simply wasn't discovered until relatively recently in human history. Earlier number systems such as Greek/Roman numerals don't have a way of representing it.
Re:Why do arrays start at 0? by froh on Thursday August 25, 2022 @01:48AM (#32590011)
when I count apples I happily start with one. it's "natural" to label the first thing "1".

or how would you count, say, 3 Apples? or three legionaries?

Re:Why do arrays start at 0? by iopq on Thursday August 25, 2022 @02:10AM (#32590172)
Counter-example:

You have to remove all desks from #20 to #30, how many desks are there?

Oh, it's 11 desks, I see. This is because we used INCLUSIVE indexing, instead of semi-open interval.

If we use semi-open intervals, we don't include the last item, so that we can write

    match index {
        0..10 => println!("first ten"),
        10..20 => println!("next ten"),
        _ => println!("something else"),
    }
but this forces us to start our intervals at 0, otherwise we would have to write 1..11 which would be awkward
Re:Why do arrays start at 0? by maxdamantus on Thursday August 25, 2022 @04:18AM (#32591028)
Indeed. End exclusion is advantageous because we don't need to add or subtract 1 when constructing or interpreting ranges. Forgetting to add or subtract 1 is basically why off-by-one errors exist (and doing add rather than subtract or vice versa is probably why off-by-two errors exist).

`a..(a+n)` is an n-length range, not an (n + 1)-length range (oh look, an "add 1" operation!).

And an empty range is denoted by `a..a`, not `a..(a-1)` (oh look, a "subtract 1" operation!).

Re:Why do arrays start at 0? by gernb on Thursday August 25, 2022 @04:29AM (#32591115)
doesn't swift use inclusive indexing? 1...10 includes 10
Re:Why do arrays start at 0? by maxdamantus on Thursday August 25, 2022 @04:51AM (#32591244)
Haven't used Swift, but looking at the documentation, it seems to support `...` for end-inclusive and `..<` for end-exclusive.

Looking at the Swift standard library, end-exclusive ranges seem to be far more commonly used:

  ~/build/swift$ git grep -e '\.\.< *[a-zA-Z0-9_(]' --and --not -e '^ *//' -- stdlib/\*.swift | wc -l
  599

  ~/build/swift$ git grep -e '\.\.\. *[a-zA-Z0-9_(]' --and --not -e '^ *//' -- stdlib/\*.swift | wc -l
  112
Re:Why do arrays start at 0? by maxdamantus on Thursday August 25, 2022 @04:05AM (#32590938)
[0, 1, 2] would be how I would refer to my 3 apples. Each apple is identified by the number of apples that precede it.

I don't think it's inherently more natural to start from 1, just conventional. Disregarding history/convention, I think it would be more natural to use the lowest available natural number.

Back in Roman times, the lowest natural number that people were aware of was "1", so obviously they started counting from that number.

Our understanding of and use of mathematics has evolved since then, and accordingly there are fields such as computer science and combinatorics where there are clear advantages to starting from the smallest number (zero). In virtually all other cases, the reason "1" seems more natural is because that's the way it's been done historically.

It seems that when labelling those apples using a 1-based count, the logic is basically: each apple is identified by the number of apples that precede it, plus 1. The reason for the "plus 1" is that that was your starting number, but it could have easily been 2 or 3. If you instead start from 0, you can omit the "plus X" logic, just as I omitted the "+ 1" and "- 1" logic in my year/century formulae when moving from 1-based to 0-based counting.

Re:Why do arrays start at 0? by topaz0 on Thursday August 25, 2022 @10:02PM (#32602521)
The romans weren't the only people who knew how to count back then... And how many of the other (presumably arbitrary) choices for smallest number were zero? And why did it take over one thousand years to come up with ordinal "zeroth" after we knew about cardinal "zero"?

> It seems that when labeling ... number ... that precede it, plus one.

I don't think so. It's the number that you have counted once you've counted that one.

Re:Why do arrays start at 0? by maxdamantus on Friday August 26, 2022 @01:02AM (#32603537)
How do you write zero in Roman numerals? Answer: you can't. Even though they used their number system for adding amounts of money, they hadn't figured out "cardinal zero" yet.

It was introduced into western mathematics through Fibonacci in the 1200s at the same time that Hindu-Arabic numerals were adopted, which use "0" as a placeholder (compare this to earlier Greek numerals which work similarly to the system we use today but without placeholders and using different sets of symbols for the different places—and of course no way of representing zero).

Re:Why do arrays start at 0? by topaz0 on Friday August 26, 2022 @10:34AM (#32608056)
That's what I'm talking about (though I guess the "thousand years" estimate was slightly high). Why did it take hundreds of years after the introduction of zero to get "the zeroth element of a sequence", which is quite a new usage, and even now is confined to specialized fields?
Re:Why do arrays start at 0? by dahart on Wednesday August 24, 2022 @08:01PM (#32587552)
The well known “Numerical Recipes in C 2nd ed.” talks about how the first edition promoted exactly this - offset the pointer after allocation and you can use any indexing origin you want. And they promoted 1-indexing and wrote all the algorithms as such, before switching everything to 0-indexing for the 2nd edition.

I agree with you the subtraction instruction is not exorbitant, especially considering malloc is much more expensive and always has been.

Re:Why do arrays start at 0? by dahart on Wednesday August 24, 2022 @08:14PM (#32587655)
Integer add/subtract never really took a long time. Slower than today’s sub-nanosecond, and it depends on the computer, but there were CPUs in the mid 1960s doing millions of instructions per second. They weren’t crazy slow, they were just crazy expensive.
Re:Why do arrays start at 0? by dboreham on Wednesday August 24, 2022 @11:00PM (#32588925)
A million, perhaps. But you're talking about adding one instruction to every single data access. Plus your code bloats up with all those extra dec instructions. BCPL and C were created for departmental and lab computers like the pdp-9.

Note, for example that the mighty 6502 succeeded in the market because its designers had heard from customers that the $100 price for the 6800 was too much, so they removed some instructions and addressing modes to get the die small enough that it could be sold for $20.

Re:Why do arrays start at 0? by dahart on Thursday August 25, 2022 @09:31AM (#32593460)
> But you're talking about adding one instruction to every single data access.

No, you misunderstood. You subtract one from the pointer at allocation time. You don’t add an instruction every time you access.

Re:Why do arrays start at 0? by topaz0 on Thursday August 25, 2022 @10:04PM (#32602541)
And even if it was an instruction for every access, memory access is much more expensive than and add (at least nowadays -- I don't know if that's always been true).
Re:Why do arrays start at 0? by dahart on Friday August 26, 2022 @08:46AM (#32606694)
Similarly I like to think about it as the add/subtract instruction is very small compared to malloc (and maybe could be included in a custom allocator for free). So the one extra instruction will always be somewhere between relatively minor to negligible and unnoticed.

Yeah today memory access on a cache miss is really expensive. I think memory was normally running at a different clock rate than the CPU even 50-60 years ago, so has always been something where you can’t touch memory every instruction. The latency of a cache miss wasn’t nearly as bad in the past. When the data cache was invented, a main memory access was like 4 times slower than register access. Today, main memory access can be more than a hundred time slower, sometimes it can approach a thousand times slower, which is why we always have multi-level caches now. And memory latency is still getting worse. If someone figured out how to make memory faster without increasing the cost or the energy consumption, they’d be rich! ;)

Re:Why do arrays start at 0? by kitkat_new on Wednesday August 24, 2022 @06:20PM (#32586565)
would it? or would it be just a differently wired circuit?
Re:Why do arrays start at 0? by fsckboy on Wednesday August 24, 2022 @07:33PM (#32587313)
the "differently wired circuit" would be an extra stage of logic computing a carry all the way from lsb to msb (an ALU outside the ALU?) and would contribute a fair bit of extra time. Easier to just use the ALU to do it, which is inserting an extra instruction, also a time waster.
Re:Why do arrays start at 0? by codebje on Wednesday August 24, 2022 @08:47PM (#32587896)
If you're already doing base index addressing (eg, [SI + DI]) you're already using the ALU to compute your memory address, and it's not _that_ much extra wiring to have the ALU support either a constant power-of-two displacement or even a three-way addition; a constant displacement for a basic ripple adder is a XOR and an AND NOT, eg. The x86 family does three-way addition (and a shift) to support "[ESI] [EBX * scale + displacement]".
Re:Why do arrays start at 0? by TuringTest on Thursday August 25, 2022 @02:32AM (#32590315)
It's not just the extra wiring, but the extra time; each additional computation layer in an integrated circuit introduced delays that would made the operation noticeably slower.
Re:Why do arrays start at 0? by codebje on Wednesday August 24, 2022 @08:30PM (#32587776)
Well, maybe, if the specific use case of 1-based arrays was all that got supported.

It'd be a pretty huge waste of processor design space to do that, though: if an HLL wants to support n-based arrays for n != 0 it can just store the base pointer with the initial index offset already applied.

Early 8-bit processors had a pretty poor set of addressing modes: you couldn't even expect [R1 + R2] let alone [R1 + R2 * scale + displacement], so random access into an array would be a multi-instruction task. By the time of the 80386 base scaled index addressing with displacement was in the CPU core, but it's not _free_ - there's an additional byte in the instruction encoding for the displacement, so even if the CPU computes the displacement with no additional clock cycles you'll have a latency cost for fetching the byte.