SOLVED: Unexpected array size

I’m really puzzled right now by what’s happening when I declare an empty array with N elements. Every time I check the size of the array, it has twice as many elements as I declared in it. Also, when the values of an array are not initialized, should I find a random number in there? Below is my code, followed by the output:

void setup(){
  
  Serial.begin(9600);
  
  int num_of_elements = 10;
  int myarray[num_of_elements];
  
  delay(3000);
  int sizeA = sizeof(myarray);
  Serial.print("myarray has a size of ");
  Serial.println(sizeA);
  
  int r;
  for(r=0;r<sizeof(myarray);r++)
  {
    Serial.print("Element ");
    Serial.print(r);
    Serial.print(" = ");
    Serial.println(myarray[r]);
  }
}

void loop(){}

Output:

myarray has a size of 20
Element 0 = 2816
Element 1 = -504
Element 2 = -3832
Element 3 = 512
Element 4 = 2145
Element 5 = 241
Element 6 = 4574
Element 7 = 2053
Element 8 = -81
Element 9 = 29696
Element 10 = 104
Element 11 = -8704
Element 12 = 1297
Element 13 = -3839
Element 14 = -17917
Element 15 = 26624
Element 16 = 23296
Element 17 = 31085
Element 18 = 29281
Element 19 = 24946

int sizeA = sizeof(myarray);

sizeof is the number of bytes an object consumes, not the number of elements (unless the elements are bytes)

There's a clue: you declared the array to have ten elements - why would you think it could have twenty?

I didn't think it could have 20. I was confused when I went to print each element in a loop and got 20 numbers back. Why is it returning a value when I call an element that is out of it's index?

Because C just doesn't care about array bounds.
It's just a pointer and an offset.

Try this

void setup() 
{
  Serial.begin(115200);
  int myarray[10];

  Serial.print("myarray takes this number of bytes ");
  Serial.println(sizeof(myarray));
  
  int sizeOfEachElement = sizeof(myarray[0]);
  int numberOfElements = sizeof(myarray) / sizeOfEachElement;
  Serial.print("myarray has this number of elements ");
  Serial.println(numberOfElements);
}

void loop() 
{
}

smahoo:
I'm really puzzled right now by what's happening when I declare an empty array with N elements. Every time I check the size of the array, it has twice as many elements as I declared in it. Also, when the values of an array are not initialized, should I find a random number in there? Below is my code, followed by the output:

sizeof() tells you how many BYTES are used by the array. A char or uint8_t array will have the same "sizeof()" as it's elements.

But, an array of int or uint16_t will have 2X the size of it's elements because each element has 2 bytes in it!

Use this code to solve all your problems:

absolute_array_size = sizeof (array);
bytes_in_each_element = sizeof (*array);
number_of_elements = (sizeof (array) / sizeof (*array));

Hope this helps.

You’ll see this idiom used:

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

// ...some code...
  int myarray[10];

   for (int i = 0; i < elementCount(myarray); i++) {
      // ...loop code...
   }
//  ...more code...

The macro takes the memory space allocated to the object and divides it by the size of one element in the object. The result is the number of elements in the object. This has the advantage of automatically adjusting the element count anytime you alter the array size.

Thanks for the help everyone.

…but be aware that the macro won’t work inside a function if the array is a function parameter.

Because C just doesn’t care about array bounds.
It’s just a pointer and an offset.

It does care, when using the pointer its the programmer who doesn’t care. :slight_smile:

…but be aware that the macro won’t work inside a function if the array is a function parameter.

Unless you keep the type as an array.

void Foo( int (&ref)[ 10 ] ){
}

//Or even better
template< typename T, unsigned N >
void Foo( T (&ref)[ N ] ){
}

The template version will even work with multidimensional arrays.

It does care, when using the pointer its the programmer who doesn't care.

No, C really doesn't care.
Negative out of bounds or positive out of bounds; they're all the same.

When accessing elements for sure, you can be as dangerous as you like, however an array is not quite a pointer ;), Only the raw array name resolves to a pointer to the first element.
Standard indices are of type size_t, so there is no negative indices, just dangerous use of variable overflow.

The compiler maintains the index information as you can see with the template. It doesn't just guess the value of N, which shows it does care, very much. :slight_smile:

More importantly, by using a template it should be clear that Arduino is C++, not C

so there is no negative indices,

BS

const int array [] = {-1001, -902, -803, -704, -605, 
                       -506, -407, -308, -209, -110, 
                       0, 
                       101, 202, 303, 404, 505, 606};
                       
const int* offsetArray = &array [10];
                       
void setup ()
{
  Serial.begin (115200);
  for (int i = -10; i < 6; i++) 
    Serial.println (offsetArray [i]);
}

void loop ()
{}

You integer maths occurs before you access the array, the value is still positive, say the address was 0x64, 100 + (-10) == 90

Start your pointer at location 0x00 and take note that you do not read non-existent negative data.

the value is still positive

I'd believe that if I hadn't seen it work on a processor with a signed address space, where RAM was in a very negative address range.

AWOL:

the value is still positive

I'd believe that if I hadn't seen it work on a processor with a signed address space, where RAM was in a very negative address range.

Uh huh, got any documentation.
Signed values used in conjunction with unsigned values are converted to unsigned.

The processor you used either wasn't programmed in C++, or is a non-standard version of the language, and could therefore do anything. size_t is unsigned set by the standard, which is tied to sizeof.

econjack:
You’ll see this idiom used:

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

// …some code…
  int myarray[10];

for (int i = 0; i < elementCount(myarray); i++) {
      // …loop code…
   }
//  …more code…




The macro takes the memory space allocated to the object and divides it by the size of one element in the object. The result is the number of elements in the object. This has the advantage of automatically adjusting the element count anytime you alter the array size.

sizeof (x[0]) and sizeof (*x) are the same thing. :slight_smile:

AWOL:
...but be aware that the macro won't work inside a function if the array is a function parameter.

Even if it does work in a certain usage, imagine the disaster that would occur if someone passed "x++" to that macro!

Even if it does work in a certain usage, imagine the disaster that would occur if someone passed "x++" to that macro!

I agree, that is why using a parameter which is an array type is far better, the value is there already, no need for the macro at all.

pYro_65:

AWOL:

the value is still positive

I'd believe that if I hadn't seen it work on a processor with a signed address space, where RAM was in a very negative address range.

Uh huh, got any documentation.
Signed values used in conjunction with unsigned values are converted to unsigned.

The processor you used either wasn't programmed in C++, or is a non-standard version of the language, and could therefore do anything. size_t is unsigned set by the standard, which is tied to sizeof.

That's a good point; C++ wasn't so common back then, and the only sensible options on the processor family were C (where negative indices were used to some effect) and occam, which of course, had bounded arrays and didn't have pointers.
I believe the architecture, sans links, lived on in one of ST's embedded processor families, so it would surprise me if C++ wasn't available some time.