Search code examples
c++arraysarduinoavrprogmem

Arduino accessing array produces unexpected resuslts


I am trying to read the length of some strings in flash memory on my Arduino UNO. The array string_table is giving me problems, if I get the index of it with something the compiler optimises to a constant, then I get the expected value. If I access it with something that is a variable at run time, then I get a different answer each time I do so.

I don't think this is specific to Arduino, as I don't seem to be calling any Arduino specific functionality.

Code:

#include <avr/pgmspace.h>

// Entries stored in flash memory
const char entry_0[] PROGMEM = "12345";
const char entry_1[] PROGMEM = "abc";
const char entry_2[] PROGMEM = "octagons";
const char entry_3[] PROGMEM = "fiver";

// Pointers to flash memory
const char* const string_table[] PROGMEM = {entry_0, entry_1, entry_2, entry_3};

void setup() {
  Serial.begin(115200);
  randomSeed(analogRead(0));

  int r = random(4);
  Serial.print("random key (r) : ");
  Serial.println(r);
  Serial.print("Wrong size for key = ");
  Serial.print(r);
  Serial.print(" : ");
  Serial.println(strlen_P(string_table[r]));
  int i = 1;
  Serial.print("Correct size for key = ");
  Serial.print(i);
  Serial.print(" : ");
  Serial.println(strlen_P(string_table[i]));
  Serial.println("=====");

  Serial.println("Expected Sizes: ");
  Serial.print("0 is: ");
  Serial.println(strlen_P(string_table[0]));
  Serial.print("1 is: ");
  Serial.println(strlen_P(string_table[1]));
  Serial.print("2 is: ");
  Serial.println(strlen_P(string_table[2]));
  Serial.print("3 is: ");
  Serial.println(strlen_P(string_table[3]));
  Serial.println("++++++");

  Serial.println("Wrong Sizes: ");
  for (i = 0; i < 4; i++) {
    Serial.print(i);
    Serial.print(" is: ");
    Serial.println(strlen_P(string_table[i]));
  }
  Serial.println("------");
  delay(500);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Results:

random key (r) : 1
Wrong size for key = 1 : 16203
Correct size for key = 1 : 3
=====
Expected Sizes: 
0 is: 5
1 is: 3
2 is: 8
3 is: 5
++++++
Wrong Sizes: 
0 is: 0
1 is: 11083
2 is: 3
3 is: 3
------

Solution

  • From avr-libc - source code:

    strlen_P() is implemented as an inline function in the avr/pgmspace.h header file, which will check if the length of the string is a constant and known at compile time. If it is not known at compile time, the macro will issue a call to __strlen_P() which will then calculate the length of the string as normal.

    That could explain it (although the call to __strlen_P() should have fixed that).

    Maybe use the following instead:

    strlen_P((char*)pgm_read_word(&(string_table[i])));
    

    Or maybe try using strlen_PF().