[SOLVED] Wrong values when accessing multidimensional array

Hi,

I ran into an issue which I can’t figure out and I wondered if you guys would be able to shed some light on this.

I’ve got a multidimensional array with bytes which maps a number (0-9) to a collection of leds with id’s between 0 and 7. This code is inside a separate library.

#define NUM_NUMBERS 10
#define NUM_PARTS 7

byte _num;
const byte bMap[NUM_NUMBERS][NUM_PARTS] = {
  {0, 1, 2, 4, 5, 6}, // 0
  {2, 5}, // 1
  {0, 2, 3, 4, 6}, // 2
  {0, 2, 3, 5, 6}, // 3
  {1, 2, 3, 5}, // 4
  {0, 1, 3, 5, 6}, // 5
  {0, 1, 3, 4, 5, 6}, // 6
  {0, 2, 5}, // 7
  {0, 1, 2, 3, 4, 5, 6}, // 8
  {0, 1, 2, 3, 5, 6} // 9
};

void Digit::set (byte num)
{
  if (_num != num)
  {
    _num = num;

    Serial.println("---");
    Serial.print("Values [");
    Serial.print(num);
    Serial.print("]: ");

    for (byte a = 0; a < sizeof(bMap[num]); a++)
    {
      Serial.print(bMap[num][a]);
      Serial.print(", ");
    }
    Serial.println(';');
  }
}

The problem is, whatever index I try to fetch all I get back are zero’s.

For example:

Digit::set(2); // returns: Values [2]: 0(0), 0(1), 0(2), 0(3), 0(4), 0(5), 0(6), ;

Here’s one thing though: I was using an array of int’s before this and I got some weirder results. Because sizeof(bMap[num]) returns 14 when using int’s I got double the results in my output. Somewhere in the results it started returning some other values than 0. Thos values weren’t in this array, but in a different array (even a different class).

I know that when using dynamically sized arrays you are actually accessing bytes in the memory, but since I allocated all the necessary space for the array this shouldn’t be the case.

For debugging purposes I’ve put in some print statements, but eventually I will do something with the values inside the for loop in the Digit::set() function.

Does anybody have an idea about what might be happening here?

[EDIT]
Turned out I was defining 2 separate variables and accessing the wrong one in my code. This resulted in no values being returned at all. The solution for this can be found on the second page of this thread.
[/EDIT]

– Wouter

Post your code.

#define NUM_NUMBERS 10
#define NUM_PARTS 7

byte _num;
const byte bMap[NUM_NUMBERS][NUM_PARTS] = {
  {0, 1, 2, 4, 5, 6}, // 0
  {2, 5}, // 1
  {0, 2, 3, 4, 6}, // 2
  {0, 2, 3, 5, 6}, // 3
  {1, 2, 3, 5}, // 4
  {0, 1, 3, 5, 6}, // 5
  {0, 1, 3, 4, 5, 6}, // 6
  {0, 2, 5}, // 7
  {0, 1, 2, 3, 4, 5, 6}, // 8
  {0, 1, 2, 3, 5, 6} // 9
};

void set (byte num)
{
  if (_num != num)
  {
    _num = num;

    Serial.println("---");
    Serial.print("Values [");
    Serial.print(num);
    Serial.print("]: ");

    for (byte a = 0; a < sizeof(bMap[num]); a++)
    {
      Serial.print(bMap[num][a]);
      Serial.print(", ");
    }
    Serial.println(';');
  }
}
void setup ()
{
  Serial.begin (115200);
  for (int i = 0; i < 10; i++)
    set (i);
}

void loop ()
{
}

Produces the expected output.

Here’s one thing though: I was using an array of int’s before this and I got some weirder results. Because sizeof(bMap[num]) returns 14 when using int’s I got double the results in my output. Somewhere in the results it started returning some other values than 0. Thos values weren’t in this array, but in a different array (even a different class).

sizeof returns a count of bytes not elements. ints are 2 bytes, resulting in twice the size of the result using char/byte.

pYro_65:

Here’s one thing though: I was using an array of int’s before this and I got some weirder results. Because sizeof(bMap[num]) returns 14 when using int’s I got double the results in my output. Somewhere in the results it started returning some other values than 0. Thos values weren’t in this array, but in a different array (even a different class).

sizeof returns a count of bytes not elements. ints are 2 bytes, resulting in twice the size of the result using char/byte.

I know, that’s why I switched to bytes eventually (plus that I didn’t need any values bigger than 7). Before that I used (sizeof(bMap[num])/sizeof(byte) - 1) to get the size of the array.

Still can’t see your code.

AWOL:
Post your code. ...
Produces the expected output.

I’ve organised my code into a few libraries to keep functionality separated and clear. Not all functionality is there yet and I still need to refactor some code, but the part that needs to access that array should be functional. I’ve added all my code as a gist.

Basically I’m creating a digital clock with 2 leds per ‘part’ (see attached image) and as a disclaimer: this is my first Arduino project and my very first time working with C++ (though I have experience with a few other languages).

– Wouter

AboutWout:
I know, that’s why I switched to bytes eventually (plus that I didn’t need any values bigger than 7). Before that I used (sizeof(bMap[num])/sizeof(byte) - 1) to get the size of the array.

If you want to use any size (data type) you like and not care about the nitty gritty, here is a fun little example that uses objects to gain a little more info from your arrays.

#define NUM_NUMBERS 10
#define NUM_PARTS 7

const byte bMap[NUM_NUMBERS][NUM_PARTS] = {  {0, 1, 2, 4, 5, 6}, {2, 5}, {0, 2, 3, 4, 6}, {0, 2, 3, 5, 6}, {1, 2, 3, 5}, {0, 1, 3, 5, 6}, {0, 1, 3, 4, 5, 6}, {0, 2, 5}, {0, 1, 2, 3, 4, 5, 6}, {0, 1, 2, 3, 5, 6} };

template< typename T > struct ArraySize;
template< typename T, size_t N > struct ArraySize< T[N] > { enum{ Size = N }; }; 
template< typename T, size_t A, size_t N > struct ArraySize< T[A][N] >{ enum{ InnerSize = A, OuterSize = N }; };

void setup() {
  Serial.begin(9600);
  Serial.print( "Size of inner index: " );
  Serial.println( ArraySize< typeof( bMap ) >::InnerSize );
  Serial.print( "Size of outer index: " );
  Serial.println( ArraySize< typeof( bMap ) >::OuterSize );
}

void loop(){}

http://sscce.org/

pYro_65:

AboutWout:
I know, that’s why I switched to bytes eventually (plus that I didn’t need any values bigger than 7). Before that I used (sizeof(bMap[num])/sizeof(byte) - 1) to get the size of the array.

If you want to use any size (data type) you like and not care about the nitty gritty, here is a fun little example that uses objects to gain a little more info from your arrays.

#define NUM_NUMBERS 10

#define NUM_PARTS 7

const byte bMap[NUM_NUMBERS][NUM_PARTS] = {  {0, 1, 2, 4, 5, 6}, {2, 5}, {0, 2, 3, 4, 6}, {0, 2, 3, 5, 6}, {1, 2, 3, 5}, {0, 1, 3, 5, 6}, {0, 1, 3, 4, 5, 6}, {0, 2, 5}, {0, 1, 2, 3, 4, 5, 6}, {0, 1, 2, 3, 5, 6} };

template< typename T > struct ArraySize;
template< typename T, size_t N > struct ArraySize< T[N] > { enum{ Size = N }; };
template< typename T, size_t A, size_t N > struct ArraySize< T[A][N] >{ enum{ InnerSize = A, OuterSize = N }; };

void setup() {
  Serial.begin(9600);
  Serial.print( "Size of inner index: " );
  Serial.println( ArraySize< typeof( bMap ) >::InnerSize );
  Serial.print( "Size of outer index: " );
  Serial.println( ArraySize< typeof( bMap ) >::OuterSize );
}

void loop(){}

Thanks for the example pYro_65, once I get this working I will certainly take a look at your suggestion.

– Wouter

I would suggest that there is a fundamental flaw in the method you are using. Are you sure you’re really going about this the right way?

Take the following piece of code (written for standard C on a PC) adapted from yours:

#include <stdio.h>
#include <stdint.h>

#define NUM_NUMBERS 10
#define NUM_PARTS 7

const uint8_t bMap[NUM_NUMBERS][NUM_PARTS] = {
    {0, 1, 2, 4, 5, 6},
    {2, 5},
    {0, 2, 3, 4, 6},
    {0, 2, 3, 5, 6},
    {1, 2, 3, 5},
    {0, 1, 3, 5, 6},
    {0, 1, 3, 4, 5, 6},
    {0, 2, 5},
    {0, 1, 2, 3, 4, 5, 6},
    {0, 1, 2, 3, 5, 6}
};

int main() {
    int outer;
    int inner;
    for (outer = 0; outer < NUM_NUMBERS; outer++) {
        for (inner = 0; inner < NUM_PARTS; inner++) {
            printf("%d, ", bMap[outer][inner]);
        }
        printf("\n");
    }
}

What do you think the output of that would be? I’ll tell you now, it won’t be:

0, 1, 2, 4, 5, 6, 
2, 5, 
0, 2, 3, 4, 6, 
0, 2, 3, 5, 6, 
1, 2, 3, 5, 
0, 1, 3, 5, 6, 
0, 1, 3, 4, 5, 6, 
0, 2, 5, 
0, 1, 2, 3, 4, 5, 6, 
0, 1, 2, 3, 5, 6,

because that’s not how arrays work in C. Every array slice has a fixed size. In this case each array slice is itself an array, which has a fixed size. You’re partially filling that array, and not filling other parts. So what is in the parts that are not being filled? Well, as this is a global array, I’d say there’s a good chance it’s filled with 0. Indeed, the actual output of that program is:

0, 1, 2, 4, 5, 6, 0, 
2, 5, 0, 0, 0, 0, 0, 
0, 2, 3, 4, 6, 0, 0, 
0, 2, 3, 5, 6, 0, 0, 
1, 2, 3, 5, 0, 0, 0, 
0, 1, 3, 5, 6, 0, 0, 
0, 1, 3, 4, 5, 6, 0, 
0, 2, 5, 0, 0, 0, 0, 
0, 1, 2, 3, 4, 5, 6, 
0, 1, 2, 3, 5, 6, 0,

Yes, lots of extra 0’s.

And the effect - assuming you can get your program to work with it - is? Simple. Take the number 1, for example. Your program would turn on pins 2, 3, 0, 0, 0, 0 and 0. So instead of a 1 you’d get a 7, or maybe a J or maybe even a ┤, depending on which segment pin 0 was connected to.

So what would be a better way? Well, instead of listing which pins are on, why not list all the pins and whether they are on or not? Instead of a list of “2,3”, why not a list of “0, 0, 1, 1, 0, 0, 0, 0”? Or better still, why not compress them down into a single byte? A digit can then be represented as something like, say 0b00001100, and you can use bitwise operators (or even a union if you really want) to access individual bits.

For example, I have a library for an I2C connected dual 7-segment display, and in the top of that I have:

const unsigned char digits[] = {
  0b01111110,
  0b00010010,
  0b10111100,
  0b10110110,
  0b11010010,
  0b11100110,
  0b11101110,
  0b00110010,
  0b11111110,
  0b11110110,
};

Then I use them as:

void I2CDisplay::setLeftDigit(unsigned char d) {
  unsigned char i;
  d = d % 10;
  d = digits[d];
  d |= this->leftDecimal ? 1 : 0;
  for (i=0; i<8; i++) {
    this->digitalWrite(i, d & (1 << i) ? HIGH : LOW);
  }
}

No messy 2-dimentional arrays. For ease of mapping you can store your IO pin arrangement in an array:

static const uint8_t ioPins[] = {2, 3, 5, 0, 4, 8, 6, 7};

and use those to map between bits in your digit bytes and actual segments.

Hi majenko,

majenko:
I would suggest that there is a fundamental flaw in the method you are using. Are you sure you’re really going about this the right way?

As noted above I’m just starting out with Arduino/C++ coding, so my methods are still a bit unconventional (or maybe even wrong) for this language. So any advice I can get I will gladly accept :slight_smile:

The issue with the extra zero’s in the array was something I was definitely concerned about, but if I implement your method I won’t have to worry about that anymore. I’ll do some reading up on bitshifts to see if I understand what exactly you are doing so I can comfortably use it in my own code (I never use code I don’t understand).

Just one question: is there a particular reason you’re using an unsigned char to hold the binary value? Isn’t byte preferred on the Arduino platform as they are exactly the same?

– Wouter

AboutWout: Hi majenko,

majenko: I would suggest that there is a fundamental flaw in the method you are using. Are you sure you're really going about this the right way?

As noted above I'm just starting out with Arduino/C++ coding, so my methods are still a bit unconventional (or maybe even wrong) for this language. So any advice I can get I will gladly accept :)

The issue with the extra zero's in the array was something I was definitely concerned about, but if I implement your method I won't have to worry about that anymore. I'll do some reading up on bitshifts to see if I understand what exactly you are doing so I can comfortably use it in my own code (I never use code I don't understand).

Just one question: is there a particular reason you're using an unsigned char to hold the binary value? Isn't byte preferred on the Arduino platform as they are exactly the same?

– Wouter

I use unsigned char, or uint8_t, but I never ever ever use byte. Byte is not a standard C or C++ data type - it is specific to the Arduino. As you say, they are the same, so I always choose one that is standard over the non-standard one. And "unsigned char" is more standard than "uint8_t", though I have yet to find a system that doesn't know about uint8_t.

Hi majenko,

void I2CDisplay::setLeftDigit(unsigned char d) {
  unsigned char i;
  d = d % 10;
  d = digits[d];
  d |= this->leftDecimal ? 1 : 0;
  for (i=0; i<8; i++) {
    this->digitalWrite(i, d & (1 << i) ? HIGH : LOW);
  }
}

I’ve been looking at your code and I am unsure what the this->leftDecimal function does. Could you perhaps post the code of that function?

– Wouter

Well, at least I figured out why initially I wasn’t getting any values back: the _bMap variable is out of scope.

With the new (binary) method I was getting zero’s back as well, so I moved the array into the Digit::set() to be sure and I got back the correct values. Now I only need to figure out why the variable is out of scope and how to fix it.

– Wouter

AboutWout:
Hi majenko,

void I2CDisplay::setLeftDigit(unsigned char d) {

unsigned char i;
  d = d % 10;
  d = digits[d];
  d |= this->leftDecimal ? 1 : 0;
  for (i=0; i<8; i++) {
    this->digitalWrite(i, d & (1 << i) ? HIGH : LOW);
  }
}




I've been looking at your code and I am unsure what the `this->leftDecimal` function does. Could you perhaps post the code of that function?

– Wouter

this->leftDecimal isn’t a function, it’s a variable. If it’s anything but 0 then it will turn on the decimal point bit of the byte (bit 0 in my case).

AboutWout: Well, at least I figured out why initially I wasn't getting any values back: the _bMap variable is out of scope.

With the new (binary) method I was getting zero's back as well, so I moved the array into the Digit::set() to be sure and I got back the correct values. Now I only need to figure out why the variable is out of scope and how to fix it.

– Wouter

Is it defined in the same Compilation Unit as where it's used?

A const value is automatically flagged as static (which gives it "internal" linkage scope) unless it's explicitly flagged as "extern".

extern const unsigned char digits[];
const unsigned char digits[] = { ... };

majenko: this->leftDecimal isn't a function, it's a variable. If it's anything but 0 then it will turn on the decimal point bit of the byte (bit 0 in my case).

Ah, okay. In that case I can probably leave that part out since my values won't have any decimals in the first place. Thanks!

– Wouter

majenko:
Is it defined in the same Compilation Unit as where it’s used?

A const value is automatically flagged as static (which gives it “internal” linkage scope) unless it’s explicitly flagged as “extern”.

The _bMap constant is defined in the header file:

// Digit.h

#include <Arduino.h>

#ifndef Digit_h
#define Digit_h

#define NUM_NUMBERS 10
#define NUM_PARTS 7
#define NUM_LEDS 2

class Digit
{
  public:
    void set(byte num);
    void clear();
    void setPin(byte pin);
    void setLeds(byte leds_c[NUM_PARTS][NUM_LEDS]);

  private:
    byte _num;
    byte _pin;
    byte _leds[NUM_PARTS][NUM_LEDS];
    byte _all[NUM_PARTS * NUM_LEDS];
    const unsigned char _bMap[NUM_NUMBERS];
    void _part_on (byte part);
    void _part_off (byte part);
    void _all_parts_off ();

};
#endif

And it has its values assigned and used in the .cpp file:

// Digit.cpp
#include <Digit.h>

const unsigned char _bMap[NUM_NUMBERS] = {
  0b01110111, // 0
  0b00100100, // 1
  0b01011101, // 2
  0b01101101, // 3
  0b00101110, // 4
  0b01101011, // 5
  0b01111010, // 6
  0b00100101, // 7
  0b01111111, // 8
  0b01101111  // 9
};

void Digit::set (byte num)
{
  if (_num != num)
  {
    _num = num;

    Serial.print(num);
    Serial.print(": ");
    Serial.println(_bMap[num], BIN);
    ...
  }
}

If I understand the term “Compilation Unit” correctly this should be right. It all compiles just fine, so is it really out of scope or is something else happening altogether?

– Wouter

Ah, you have two distinct _bMap's there

You have ::_bMap and Digit::_bMap.

You are assigning (and at the same time creating) ::_bMap in the .cpp file, and you are not assigning anything to the Digit::_bMap array anywhere.

I am guessing you want it contained within the class? In that case I would consider making it all static:

#include <Arduino.h>

#ifndef Digit_h
#define Digit_h

#define NUM_NUMBERS 10
#define NUM_PARTS 7
#define NUM_LEDS 2

class Digit
{
  public:
    void set(byte num);
    void clear();
    void setPin(byte pin);
    void setLeds(byte leds_c[NUM_PARTS][NUM_LEDS]);

  private:
    byte _num;
    byte _pin;
    byte _leds[NUM_PARTS][NUM_LEDS];
    byte _all[NUM_PARTS * NUM_LEDS];
    static const unsigned char _bMap[NUM_NUMBERS];
    void _part_on (byte part);
    void _part_off (byte part);
    void _all_parts_off ();

};
#endif
#include <Digit.h>

const unsigned char Digit::_bMap[NUM_NUMBERS] = {
  0b01110111, // 0
  0b00100100, // 1
  0b01011101, // 2
  0b01101101, // 3
  0b00101110, // 4
  0b01101011, // 5
  0b01111010, // 6
  0b00100101, // 7
  0b01111111, // 8
  0b01101111  // 9
};

void Digit::set (byte num)
{
  if (_num != num)
  {
    _num = num;

    Serial.print(num);
    Serial.print(": ");
    Serial.println(Digit::_bMap[num], BIN);
    ...
  }
}

By the way, you can use “[code=Filename.cpp] … [/code]” to get the filename in the code block header.

Yes, now I can access the _bMap constant!

Thanks for your help so far. Now it's on to the nitty gritty stuff with the bitwise operators :cold_sweat: though I am certain I will figure that one out eventually after playing around with it for a while.

– Wouter