sizeof() not working properly

Hello!

I’m programming a communication with my arduino. I have declared a variable size array
and afterwards I give it a size with malloc (size=3, for example).
I read and store the values and finally, when I try to read the values of inst, sizeof(inst) always returns 2.
However, if I fix the limit in the for loop to the real size of inst, it correctly prints the full vector:

byte *inst;
size = 3;
inst = (byte*) malloc(size * sizeof(byte));

int cnt = 0;
while(Wire.available()) {
    inst[cnt] = Wire.read();
    Serial.println(inst[cnt]);
    cnt++;
}

sizeof(inst); // This ALWAYS returns 2

for (int i=0; i<3; i++){
    Serial.print(inst[i]);
    Serial.print(",");
} // Result: 1,2,3

I’ve tried different sizes but sizeof always returns the same “2” value.

Any idea? Thanks in advance!

Inst is a pointer. On your arduino a pointer is two bytes, which is what sizeof is telling you. The fact that that pointer points to some memory you allocated, makes no difference. You will need to keep track of the size of that memory by other means.

2 is the size of a pointer
If only you had posted your code {sigh}

You will need to keep track of the size of that memory by other means

Ok, I can store it inside the vector, thank you!

If only you had posted your code {sigh}

#include <Wire.h>

#define SLAVE_ADDRESS 0x04

byte *inst; // Reads the instructions

void setup() {
  Serial.begin(9600);
  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);

  Serial.println("Ready!");
}

void loop() {
  delay(100);
}

// callback for received data
void receiveData(int byteCount){
  byte module = Wire.read();
  byte size = Wire.read();
  size++;
  inst = (byte*) malloc(size * sizeof(byte));
  inst[0]=module;

  int cnt = 1;
  while(Wire.available()) {
    inst[cnt] = Wire.read();
    Serial.println(inst[cnt]);
    cnt++;
  }

  for (int i=0; i<size; i++){
    Serial.println(inst[i]);
  }

  Serial.println(sizeof(inst));
  free(inst);
}

// callback for sending data
void sendData(){
  byte number= 2;
  Wire.write(number);
}

You should avoid doing lots of work in an interrupt routine. Although it’s not obvious, the receiveData and sendData are called from the ISR, so they are executing during the interrupt. The Arduino can freeze up or act weird if you take too long. Some things just won’t work, like millis(). It depends on timer interrupts to advance the clock. It is not safe to allocate memory in an interrupt routine.

In your sketch, printing from receiveData can take a long time, and allocating memory could take extra time or even fail.

It’s easy to avoid printing during the ISR. Just save the data into variables that can be used in loop.

To address the memory allocation: Since you didn’t put a limit on the inst array size, what is maximum size you would expect? Your program will be faster and more reliable if you just declare the array of that size to begin with. You are limited to 32 bytes anyway, so you could do something like this:

#include <Wire.h>

#define SLAVE_ADDRESS 0x04

static const uint8_t MAX_INST = 32;
static       uint8_t inst[ MAX_INST ]; // Holds the instructions
volatile     uint8_t cnt = 0;

void setup() {
  Serial.begin(9600);
  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);

  Serial.println("Ready!");
}

void loop() {
  if (cnt > 0) {
    cli(); // disable interrupts, so receiveData can't get called
           //    while we make a copy of the inst array.
    {
      uint8_t instCopy[ cnt ];
      memcpy( instCopy, inst, cnt );
      uint8_t cntCopy = cnt;
      cnt = 0; // reset to accumulate the next inst bytes

      sei(); // reenable interrupts

      // Now that we have a safe copy of the inst array,
      //   take all the time we need to do something with
      //   the bytes, like print them out.  New inst bytes 
      //   will get saved while we're doing this.

      Serial.print(cntCopy);
      Serial.write( ':' );
      for (uint8_t i=0; i<cntCopy; i++){ // prints the module and size bytes
        Serial.print(instCopy[i]);
        Serial.write( ' ' );
      }
      Serial.println();
    }
  }
}

// callback for received data.  This happens during an interrupt,
//   so you have to be quick.  Other interrupts are disabled during this
//   interrupt, so don't do anything that depends on interrupts being
//   enabled (e.g., don't print).  Here, they are just saved into
//   an array.  The main loop will notice that cnt > 0, and it
//   will print them from there.

void receiveData(int byteCount){
  
  while (Wire.available()) {
    uint8_t b = Wire.read();

    // Don't try to save too many bytes
    if (cnt < MAX_INST)
      inst[cnt++] = b;
  }
}

// callback for sending data
void sendData(){
  byte number= 2;
  Wire.write(number);
}

In this version of your sketch, the only thing performed in receiveData is storing the received bytes and setting the count. After the interrupt returns, and loop gets called again, the count variable will not be zero.

It also shows how disable interrupts while you make a copy of the data. You don’t want receiveData to jump in and change the array or the count while you get a copy.

But why go to that trouble? Wire is buffering the bytes for you, just like Serial… you can just read them in loop, like with Serial. Here’s a sketch that lets you use either Wire or Serial for the “interface”. Just change the #define at the top:

#include <Wire.h>

#define SLAVE_ADDRESS 0x04
#define interface Serial

static const uint8_t MAX_INST = 100;
static       uint8_t inst[ MAX_INST ]; // Holds the instructions
             uint8_t cnt  = 0;
             uint8_t size;

void setup() {
  Serial.begin(9600);
  Wire.begin(SLAVE_ADDRESS);
//Wire.onReceive(receiveData);   not needed
  Wire.onRequest(sendData);

  Serial.println("Ready!");
}

void loop() {

  if (interface.available()) {
    uint8_t b = interface.read();

    receiveData( b );

    if (cnt == 2) {
      // Just got the size byte
      size = b+1;

    } else if ((cnt >= 3) && (cnt >= size+1)) {
      // We received `size` bytes (minus the module and size bytes themselves)
      printInst();

      // Reset to accumulate another message
      cnt = 0;
    }
  }
}

void printInst()
{
  Serial.print(size);
  Serial.write( ':' );

  //  Print only the bytes we actually saved in the inst array.
  uint8_t imax = size+1;
  if (imax > MAX_INST)
    imax = MAX_INST;
  for (uint8_t i=0; i<imax; i++){ // prints the module and size bytes
    Serial.print(inst[i]);
    Serial.write( ' ' );
  }
  Serial.println();
}

void receiveData( uint8_t b )
{
  // Don't try to save too many bytes
  if (cnt < MAX_INST)
    inst[cnt] = b;

  // keep track of the received count, even if we didn't save the byte
  cnt++;
}

// callback for sending data
void sendData(){
  byte number= 2;
  Wire.write(number);
}

If you enter this in the Serial Monitor window:

1 1234567890abcdefghij!@#$%^&*()AB

… it will display this:

33:49 32 49 50 51 52 53 54 55 56 57 48 97 98 99 100 101 102 103 104 105 106 33 64 35 36 37 94 38 42 40 41 65 66

The space character is ASCII 32, so the size is 33. Then you see the ASCII value for ‘1’ (49), the space value (32), and all the way up to the right parenthesis (41). It did not save the last two bytes, because the array limit is 32, but it did wait for 34 bytes to be entered.

Make sure you select “No line ending” for your testing. An ASCII chart can help you try some out. You could use a Terminal Emulator program to send control characters, like ^A (ASCII value 1). The Serial Monitor window does not allow control characters for messages < 32 bytes.

To use it with the real Wire interface, change the #define at the top. The Wire interface would be limited to messages of 32 bytes, which means the received size byte <= 30.

Cheers,
/dev

P.S. The last sketch is very similar to the Serial Input Basics examples on the Useful Links page.

-dev:
Although it’s not obvious, the receiveData and sendData are called from the ISR…

In which case the heap routines are now off-limits outside of receiveData.

It is not safe to allocate memory in an interrupt routine.

It IS safe to allocate/free memory with in an interrupt. There is no more of a problem with the heap than with an int! Just ensure that all uses of of free() malloc() etc are made atomic. Use the avr macro atomic or turn off interrupts, do the allocate (or what ever) and then turn on again!

Mark

It IS safe to allocate/free memory with in an interrupt.

Heap routines are not interrupt-safe.

If you protect everything else in your sketch (and libraries), you could allocate/free memory without corrupting the heap. It's not recommended, because an allocation is actually a search through the heap for a chunk of the required size. It may take a while, and it may fail (returning NULL). You should always pre-allocate anything that is used by an ISR.

I should point out that you didn't know that String uses the heap. If you ever wrote a sketch that used String and also allocated memory in an ISR, it would not be safe. You would have had to protect all String uses in your sketch (and libraries).