The const array size limit is ridiculously high

By mistake, I overstepped the amount of FLASH memory when declaring a const array. But nothing happened, not even a warning. Of course, I did not enter that much values into the oversized array. As I did not find a reason for the missing error message I started an interval nesting and, at the end of the day, found a limit of 1277951 = 0x137FFF bytes, which is 39 times the amount of FLASH of the 328P. I pondered a lot but could not figure out any sense in that limit. Just to make sure, I checked compiling for the UNO R4 to find out that its limit obviously is much higher.
Can somebody enlighten me where the 137FFF comes from? Massimo Banzi's birthday date?

const long N = 0x137FFF;
const byte test1[N] = { 0, 0, 0, 123 };
const byte PROGMEM test2[N] = { 0, 0, 0, 123 };

void setup() {
  Serial.println(12345);
  Serial.println(123456);
  Serial.println(test1[1234567]);
  Serial.println(test2[1234567]);
}

void loop() {}

did you upload it?
what shows serial monitor?
because this:

const unsigned long Num = 0x137FFF;
const byte test1[Num] = { 0, 0, 0, 123 };
const byte PROGMEM test2[Num] = { 0, 0, 0, 123 };

void setup() {
  Serial.begin(115200);
  for (uint32_t i = 0; i < Num; i++ )Serial.println(test2[i]);
}

void loop() {}

compiles with warning:

: warning: iteration 32767 invokes undefined behavior [-Waggressive-loop-optimizations]
   for (uint32_t i = 0; i < Num; i++ )Serial.println(test2[i]);
                                                     ~~~~~~~^
...

text section exceeds available space in board
Sketch uses 34438 bytes (106%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.
Error compiling for board Arduino Uno.

Compiler optimization has outsmarted you.

2 Likes

These two lines defined arrays with five (no, even four) elements each.

Just four
Because you declared it as const and initialize only the first four elements - the compiler will throw out all thousands bytes of your imagined data

1 Like

@Klausj
As about the limits - the maximum index of array elements on 8bits AVR arduinos is 32767. This limit has no connection with available memory, even on Mega with its 256k Flash size, you can't define array with more than 32767 elements.

Can optimisation overcome an unused array that is too big to fit? Can it optimise it away?

If you had errors in an uncalled function, they'd still be errors is why I wonder.

a7

The array size is determined by the value of N. The list of initializers can be shorter than the actual array.

for const array this initialization is final, and it is the fact why the all other elements will be "optimized out" as unused

there are optimization on compilation stage and on linking. Error checking is during compilation, wiping the unused methods and function will done on link stage, if done at all

Yes it can.

There was no error.

THX @gfvalvo. This surprises me for some reason.

No problem no error (and no real array!):

int unusedArray[5000];

void setup() {
  unusedArray[42] = 777;
  Serial.print(unusedArray[42]);
}

void loop() {

}

I can see that there is no need for an array of 5000 elements. I marvel at the depths of the optimiser that can also see - maybe not so much here, but in other cases where.

So we can end up in a fool's paradise with code very fragile and susceptible to breaking with the most seemingly innocent changes

int unusedArray[5000];

void setup() {
  unusedArray[42] = 777;
  int n = random(512);
  Serial.print(unusedArray[n]);
}

void loop() {

}

This runs out of memory, even though the human could read it and "optimise" it by knowing that the index will never be 512 or more, so maybe the array only needs 512 elements.

So there are limits to optimising yet. I guess when chatGPT grows up a little we might see a hole new world... I like it less and less when something is changing my code, although I appreciate that w/o the optimiser our little sketches might not be so small or fast.

And there are some optimisations that are truly baffling and incorrect (well, do things you wouldn't want to do).

Time was when we spent time writing, or trying to write, optimal code. Today's large and fast machines means we need to think less about that, or about the extraordinary machinations behind the scenes of more sophisticated languages.

a7

Modern compilers optimize code to a very large extent and know all the tricks of the older programmer's generation, such as right shift by 3 bits instead of dividing by 8 :slight_smile:
So in most cases nowadays there is no point in writing critical parts of a program in assembler, the compiler will do it just as well.
Moreover, in modern books programmers are increasingly called upon to make a choice in the direction of beautiful and understandable code instead of chasing savings in core clock cycles and memory bytes.
Let's leave these tricks to the compiler, especially since their importance decreases with each generation of ever faster and more powerful processors and controllers.

As far as dividing by 4 and stuff like that, sure. But for the nonce there are still plenty of things that humans can, and should, worry about when writing code.

Unless something can fly over and get a complete idea of the intent of some code, it will be humans designing the data structures and algorithms, and the good results should always be pursued given the resources available and the tradeoffs that are identified.

I just got a text message with some videos attached that are 30 times the size of my first hard drive… many things I see happening alla time around me I would have said were frankly impossible.

Lazy and naive programming means my current desk top machine is less responsive under my fingers than my favorite from 20 years ago. No, I don't want to go back in time, but it does seem that many people have never experience a good computer, and may not even know it is (or was!) possible to have one.

a7

It's connected to the size of pointers/addresses on an AVR (16 bits.)

I’m not sure that’s what the standard states and can’t check it out for the moment.

My guess is that if you declare a const int array of size 10 and provide fewer than 10 elements, then the array will be partially initialized with the provided values, and the remaining elements will be default-initialized to zero (for globals).

The optimization stage is what can help clear things out

memory limits are imposed by the chip, not the compiler. the same compiler is used on mainframes.

it's your code. where did you get it from?

OP refers to its findings

Hi everybody,
you had a nice talk about interesting things but not about my question.
Compiling this:
const long N = 0x137FFF;
const byte test1[N] = { 0, 0, 0, 123 };
...
I get the message:
Sketch uses 1644 bytes (5%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.

Even uploading and running the sketch works without an error message.
Neither compiler nor processor are complaining.

Trying to compile this (just one array element more):
const long N = 0x137FFF+1;
const byte test1[N] = { 0, 0, 0, 123 };
...
I get a long list of error messages ending with:
exit status 1
size of array 'test1' is too large

which is correct.
Everybody knows both array sizes are much too high but still the 0x137FFF gets accepted which is 39 times the size of the actual FLASH. (If it was 42 times that value everyone knew it was a joke.) I still wonder why. Magic numbers?

I can’t test right now but willing to bet you will observe the same behavior with 7FFF and 8000

7FFF is the largest address you can use and the compiler might take that into account in the way it deals with overflow and sometimes by only taking the two LSB you save the day

@J-M-L

To prove your point :wink:

const long N = 0x137FFF;
const byte test1[N] = { 0, 0, 0, 123 };

void setup()
{
  Serial.begin(115200);

  Serial.println(sizeof(test1));
}

void loop() {}

Output:

32767