const int and memory space

In other post, a user suggest that doing this:

const byte ROWS = 4; 
const byte COLS = 4;  
char keys[ROWS][COLS] = 
{
  ...
}
byte rowPins[ROWS] = { 22, 23, 24, 25 };
byte colPins[COLS] = {26, 27, 28, 29};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

wastes data memory space, and so, is better do:

char keys[4][4] = 
{
  ...
}
byte rowPins[4] = { 22, 23, 24, 25 };
byte colPins[4] = {26, 27, 28, 29};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 4 );

He suggest too, to use this:

int freeRam () {
   extern int __heap_start, *__brkval;
   int v;
   return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
(...)
Serial.println(freeRam());

to test the free data space in RAM.

I did an "experiment", and I wrote this code:

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup() {
  Serial.begin(115200);  
  Serial.println(freeRam());
}

void loop() {}

And he return:

7994

for my MEGA.

I change it for:

const int variable = 5;
const int other = 3;

const char constChar = 124;
const char otherChar = 7;

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup() {
  Serial.begin(115200);
  Serial.println(freeRam());
}

void loop() {}

And he return me the same value: 7994.

I change it one more time to:

const int variable = 5;
const int other = 3;

const char constChar = 124;
const char otherChar = 7;

int a;
int b;

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup() {
  a = variable;
  b = other;
  Serial.begin(115200);
  
  Serial.println(freeRam());
  Serial.println();
  Serial.println(a);
  Serial.println(b);
}

void loop() {
}

And he return me:

7990

5
3

Less 4 bytes from the 2 int global variables.

So, my conclusion is that the variables const int are not placed in the the data space memory.

Is my conclusion right?

So, my conclusion is that the variables const int are not placed in the the data space memory.

Is my conclusion right?

Yes, it's little more than a better way of doing a #define

#define X 4
const int X = 4;
4

All use the same amount of memory, ie none.


Rob

luisilva:
In other post, a user suggest that doing this:

const byte ROWS = 4; 

const byte COLS = 4;  
char keys[ROWS][COLS] =
...




wastes data memory space, and so, is better do:


char keys[4][4] =

Whoever that other user is, is completely wrong. Using const like that is the same as using the actual number and is much better documentation. If you hard-code "magic" numbers like [4] [4] and later decide to change one of them to 5, it is easy to overlook other parts of the code which rely on it being 4. And particularly in this case, if you change it to [5] [4] then some of the "4" in the rest of the code will need changing and some not.

Some other user, in other thread said that this isn't true.
http://forum.arduino.cc/index.php?topic=264237.msg1864225#msg1864225

holmes4:

conclusion is that it don't uses memory at all.

err, not true. Try it with a value of 16 (or 256? not sure which is the correct value with the ARV's)

Mark

So, even don't understanding exactly what he said, I change one more time the code, to:

const int variable = 256;
const int other = 16;

const char constChar = 124;
const char otherChar = 7;

int a;
int b;

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup() {
  a = variable;
  b = other;
  Serial.begin(115200);
  
  Serial.println(freeRam());
  Serial.println();
  Serial.println(a);
  Serial.println(b);
}

void loop() {
}

and I get the result:

7990

256
16

that is, the same that I get in my last test. So, my conclusion is the same: variables const int are not placed in the the data space memory, and they don't uses memory at all.

So, my conclusion is that the variables const int are not placed in the the data space memory.

Is my conclusion right?

I think from your very limited testing, yes, that's correct.

Consider:

const int variable = 256;
const int other = 16;

const char constChar = 124;
const char otherChar = 7;

int a;
int b;

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup() {
  a = variable;
  b = other;
  Serial.begin(115200);
  
  Serial.println(freeRam());
  Serial.println();
  Serial.println(a);
  Serial.println(b);
  printVal (&constChar);
}

void loop() {
}

void printVal (const char* x)
{
  Serial.println ((int)*x, HEX);
}

And now?

Edited to correct code. (added two declarations missed when cut and pasted))

const int variable = 253;
const int other = 13;

const char constChar = 124;
const char otherChar = 7;
char variable1 = 23;
char variable2 = 24;
int a;
int b;

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

#define PRINT_VAL(x) {Serial.print (F(#x)); \
                      Serial.print (F(" = ")); \ 
                      printVal ((const char*)&x); }

void setup() 
{
  a = variable;
  b = other;
  Serial.begin(115200);
  
  Serial.print (F("freeRam  "));
  Serial.println(freeRam());
  Serial.print (F("a "));
  Serial.println(a);
  Serial.print (F("b  "));
  Serial.println(b);
  PRINT_VAL (constChar);
  PRINT_VAL (otherChar);
  PRINT_VAL (variable1);
  PRINT_VAL (variable2);
}

void loop() 
{}

void printVal (const char* x)
{
  Serial.print ((int)*x);
  Serial.print (F(" @ "));
  Serial.println ((int)x);
}

The point here is that

So, my conclusion is that the variables const int are not placed in the the data space memory.

is incorrect.
If there are no indirect references to the "const" then the compiler is free to optimise them away (usually as an immediate constant in the assembler), but if any indirect reference exists, then the compiler is usually obliged to place the "const" into RAM.

Yes AWOL is right!

Other thing that I learn in this process is that the function:

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

only have the precision of 2 bytes. (I don't realise this before)

So, my conclusion now is: variables const int are not placed in the the data space memory, and they don't uses memory at all, at least they are accessed by reference.

From the other thread:

econjack:
True, side effect bugs can be difficult to track down, but I still want to have "typeless" macros from time-to-time. For example:

#define ARRAYELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0]))

can determine the size of any defined array regardless of type. As to the side effects on this macro, it would be pretty unusual to want to modify a constant lvalue.

There are always templates:

// number of items in an array
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; }

Example:

int foo [] = { 42, 56, 78, 34, 11 };

// number of items in an array
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  Serial.print ("using #define:  ");
  Serial.println (ARRAY_SIZE (foo));
  Serial.print ("using template: ");
  Serial.println (ArraySize (foo));
   }  // end of setup
void loop () { }

Output:

using #define:  5
using template: 5

If you comment-out one and then the other method the generated code size is the same. (2278 bytes on the Uno).


Rather interestingly though, this uses more program memory (2380 bytes):

int foo [] = { 42, 56, 78, 34, 11 };

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

  Serial.print ("using literal:   ");
  Serial.println (5);
  }  // end of setup

void loop () { }

To get back to 2278 bytes I had to change the println line to read:

  Serial.println ((size_t) 5);

Now most people wouldn't think to do that. The conclusion is that salting literal values through your code not only makes it harder to read and debug, but may even use more space rather than less.

AWOL:
If there are no indirect references to the "const" then the compiler is free to optimise them away...

What's an indirect reference to a const? Can you give an example?

[quote author=Nick Gammon link=topic=264244.msg1864774#msg1864774 date=1409604836]To get back to 2278 bytes I had to change the println line to read:

  Serial.println ((size_t) 5);

[/quote]

  • Serial.println ( 5);
    The literal is signed in this example, and unsigned when cast to a size_t. This calls a different overload of print::println(), and the signed version has a little extra code to handle negative numbers.

Now most people wouldn't think to do that. The conclusion is that salting literal values through your code not only makes it harder to read and debug, but may even use more space rather than less.

You can avoid this by using an integer literal suffix, the line below should produce code equivalent to using the size_t cast:

Serial.println ( 5ul );
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  Serial.print ("using literal:   ");
  Serial.println (5ul);
  }  // end of setup

void loop () { }

2264 bytes. :slight_smile:

  Serial.println (5u);

Gives 2278 bytes.

But who writes code like that?

How many kids do you have?

Response:

5 (unsigned).

An array of constants used for linear interpolation (may have syntax errors)...

float const C[] const = ( 36, 1274, 283745 );

The compiler optimizes away the array if the array index is always a compile time constant...

C[0]

The array is brought into SRAM if the array index is variable...

for ( i=0; i < 3; ++i )
Serial.println( C [ i ] );

Edited to make example clearer. AWOL

AWOL, I fear your edits may have been lost to the ether. We appear to have been editing simultaneously. I apologize. And am finished for the night.

Good night to all!

There are (at least) 3 different places your constant could be "stored".

  1. In SRAM.

  2. In the instruction, eg Load(,4); A single instruction can be used and the data is with in the instruction.

  3. In a register. With or without an address in SRAM for the constant.

Options 3 also so applies to vars not just constants.

If your are going to look at this you need to keep an eye on the size of the code as well as the SRAM usage. The complexity/size of the program will also effect where the compiler stores things, as will the number of vars/constants.

Mark

holmes4:
There are (at least) 3 different places your constant could be "stored".

  1. In SRAM.

  2. In the instruction, eg Load(,4); A single instruction can be used and the data is with in the instruction.

  3. In a register. With or without an address in SRAM for the constant.

Options 3 also so applies to vars not just constants.

If your are going to look at this you need to keep an eye on the size of the code as well as the SRAM usage. The complexity/size of the program will also effect where the compiler stores things, as will the number of vars/constants.

Mark

Case 3 is virtually identical to case 2.
To get into a register, it had to come out of flash.

how about a case like this (exclusively reserve a register for data) - this would satisfy #3 :stuck_out_tongue:

register unsigned char a asm ("r20");

then you could reserve that specific register with -ffixed-reg on the gcc compiler :slight_smile: the point is, there are many ways to code and how to tweak things specifically to your needs. i think this thread has been informative about the different scenarios' but in my own personal preference and style, i would have used a #define in this case.

how about a case like this (exclusively reserve a register for data) - this would satisfy #3

But the data that goes in the register has to come from somewhere and the only place it can come from is flash memory.

Case 3 is virtually identical to case 2.

err No.

In case 2 (if you look at the instruction set) for example, an add would require the use of the add immediate instruction however case 3 allows the use of Add and add without carry.

If when you say

To get into a register, it had to come out of flash.

you mean, at start up. Then yes, but that is not required in case 2.

AVR instruction set http://www.atmel.com/images/doc0856.pdf

Another oddie, to me, is the the size of the immediate value is different for different instruction types. So for ADD it 0-63 but in the immediate logical AND it's 0-255.

Mark

Edit - Grrr took me a long time to write this post.

Err, very much yes.

It either came out of flash as an immediate constant (as part of an instruction), or it came out of flash as a literal constant or table entry.

i thought our discussion was on the use of "SRAM memory" :slight_smile: