Array Basics (sizeof)

Hy
Following definition in the sketch: int myCurve[11] ={0,25,35,40,45,50,55,60,65,75,100};

later in the Setup, I Do Serial.print(sizeof(myCurve))

Well, the output is not the expectet 11, it is 44. Why? Where is my basic missunderstanding of Arrays?

Later I do txInput.setExpo(myCurve)

And in this Function, I have

char simodInput::setExpo(int *expo) {
   memcpy(expo,_expo,sizeof(expo));
   Serial.println("EXPO : ");
   for (int i=0;i<sizeof(expo);i++) {Serial.println(expo[i]);}
   Serial.println("EXPO OUT: ");
   for (int i=0;i<sizeof(_expo);i++) {Serial.println(_expo[i]);}
   return 1;
}

This brings the Output "EXPO" of -1 / 25 / 35 / 40 and "EXPO OUT" of 46 Values of non-logic (to me).
Why -1 and why only 4 Values? And why cant I copy the Array?

Hopefully it is a real basic missunderstanding.

Thanks for help

Did you bother to read up on sizeof? It returns the size of teh object in bytes. The C idiom to convert this to elements is sizeof(object)/sizeof(object[0]). In the function it just gets a pointer to your array. You need to pass the number of elements as a global or another function parameter.

the size of a pointer is 4bytes (I'm assuming you are using a Due or something like if you got sizeof(int)=4).

sizeof() is not so much a function as it is a way of getting a "compile time" constant value of a size of an object in bytes.

You cannot use sizeof() in a function to get the size of an array passed to that function because at compile time the size isn't known - think about it, if you pass an array with 10 elements, then pass another array of 40 elements, how can the value of sizeof() be known at the time of compiling, and how if it is compiled in to the code can it change value during execution.

If you were to do this:

void someFunc(int array[10]){
}

Then sizeof(array) = 10 * sizeof(int). This is because you are specifying that the function can only take arrays of 10 integers - not 9, not 11, not 5232342.

sizeof() is not a function, it's an operator. In fact, you don't need to use the parentheses.

There is no way a function can know the size of the array because arrays are passed to a function by reference. That is, the function receives the memory address (lvalue) of where the array resides in memory. The only possible exception is a null terminated character array that's treated as a string.

The parens are required if the argument is a type name.

econjack:
There is no way a function can know the size of the array because arrays are passed to a function by reference. That is, the function receives the memory address (lvalue) of where the array resides in memory. The only possible exception is a null terminated character array that's treated as a string.

That is incorrect, it is not a reference to the array or a pointer to it. Passing the array name without subscript operators decays the array type to a pointer of its first element, nothing to do with the original array. Which is why the sizeof operator does not work, as you are no longer dealing with an array.

Pass pointer to first element,

void someFunc( int *ptr ){
}

someFunc( array );

Pass array by reference:

void someFunc( int (&array)[10] ){
}
//OR
void someFunc( int array[10] ){
}

someFunc( array );

Pass array by pointer.

void someFunc( int (*array)[10] ){
}

someFunc( &array );

I disagree. You state:

...it is not a reference to the array or a pointer to it. Passing the array name without subscript operators decays the array type to a pointer of its first element, nothing to do with the original array.

Even your own quote says it's a pointer. If it has nothing to do with the original array, then why is the type of the pointer required as part of the parameter being passed? The reason is because pointer operations are scaled to the data type being pointed to. Further, if it has nothing to do with the array, then why is the value passed the lvalue of the array? Indeed, it has everything to do with the array, otherwise the function wouldn't have a memory address to allow any operation to be performed on it. Indeed, if I pass in an array name and pass additional parameters for its length and rank, I can reference the array in a way that matches its original definition.

econjack:
I disagree. You state:

...it is not a reference to the array or a pointer to it. Passing the array name without subscript operators decays the array type to a pointer of its first element, nothing to do with the original array.

Even your own quote says it's a pointer. If it has nothing to do with the original array, then why is the type of the pointer required as part of the parameter being passed? The reason is because pointer operations are scaled to the data type being pointed to. Further, if it has nothing to do with the array, then why is the value passed the lvalue of the array? Indeed, it has everything to do with the array, otherwise the function wouldn't have a memory address to allow any operation to be performed on it. Indeed, if I pass in an array name and pass additional parameters for its length and rank, I can reference the array in a way that matches its original definition.

I'm sorry but you are making wild assumptions here, you can disagree, however you are wrong. It is a pointer to the first element, that is all. You can see this more clearly trying to apply your logic to a multidimensional array. I have showed you in my post above how to pass the actual array by reference or pointer, so I do not see how this is an argument.

Consider this.

int arr[][10] = {{1,2,3,4,5,6},{1,2,3,4,5,6}};

This is wrong, as using an array name without subscript operators does not return a pointer to the array.

int **ptr = arr;

like I've already said it returns a pointer to its first element.

int (*ptr)[10] = arr;

Also an int** of the same data structure would use more memory than the array!

I was referring to the statement that ...it is not a reference to the array or a pointer to it.. We were talking about array names being passed to a function.

I was referring to the statement that ...it is not a reference to the array or a pointer to it.. We were talking about array names being passed to a function.

I'm not sure where you are getting confused, the function must accept an array type to accept an array. If for example you use a pointer to an int for the parameter, an int array will decay to a pointer of its first element and you are no longer dealing with an array. Which is what my post above explains. I'm sorry for using assignments as they seem to have been confusing. But the declarations can go straight into a function parameter list if you like.

You can use pointer arithmetic on a pointer, but that does not make it an array.

Change setExpo to accept either the size of the data structure or the number of elements. Given what you have so far I would go with the size...

char simodInput::setExpo( int expo[], size_t soexpo ) {
   memcpy( expo, _expo, soexpo );
   Serial.println("EXPO : ");
   for (int i=0;i < (soexpo / sizeof(expo[0])); i++) {Serial.println(expo[i]);}
   Serial.println("EXPO OUT: ");
   for (int i=0;i < (sizeof(_expo) / sizeof(_expo[0])); i++) {Serial.println(_expo[i]);}
   return 1;
}
txInput.setExpo( myCurve, sizeof(myCurve) );

@econjack,

Here is an excerpt straight out of the ISO C++ standard. Seriously I'm not trying to BS you.

4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue
of type “pointer to T”. The result is a pointer to the first element of the array.

In C, a pointer to the array is THE SAME THING as a pointer to the first element of the array. That's because arrays are just consecutive occurrences of elements; there is no additional internal structure describing the size of the array...

Not quite.

If you dereference a real pointer to an array, you can get the size using sizeof.

If you dereference the pointer to the first element, you get... well the first element.

If you dereference a real pointer to an array, you can get the size using sizeof.

Only if it's a compile-time constant, right?
(and, are you sure? I though ((sizeof array)/sizeof(*array)) was one of those tricks for finding the number of elements in an array...)

westfw:

If you dereference a real pointer to an array, you can get the size using sizeof.

Only if it's a compile-time constant, right?
(and, are you sure? I though ((sizeof array)/sizeof(*array)) was one of those tricks for finding the number of elements in an array...)

No, its not required to be a compile time constant for arrays.

void someFunc( int n ){
  
  int arr[ n ];
  Serial.println( sizeof( arr ) / sizeof( *arr ) );
}
//...
someFunc( random( 1, 30 ) );

I think I know what people are misunderstanding here, I'll fill in a few gaps.

Arrays are not lists of values based on a type, that is what their internal representation is. Arrays are actual types too. E.g. I can create a typedef of an array.

typedef int arr_t[ 10 ];

Not only can I create variables of this type:

arr_t arr = { 1,2,3,4,5,6,7,8,9,0 };

I can use it just like every other type.

void Foo( arr_t *arrp ){
  
  Serial.println( sizeof( *arrp ) / sizeof( **arrp ) );
}
//...
Foo( &arr );

A more practical method is to use a reference to avoid dereferencing the array.

void Foo( arr_t &ref ){
  Serial.println( sizeof( ref ) / sizeof( *ref ) );
}
//...
Foo( arr );

And finally, unknown to many, you can actually return arrays using their pointer or reference.

typedef int arr_t[10];
arr_t &Foo( arr_t &ref ); //Forward dec to stop IDE moaning

arr_t arr = { 1,2,3,4,5,6,7,8,9,0 };

arr_t &Foo( arr_t &ref ){
  Serial.println( sizeof( ref ) / sizeof( *ref ) );
  return ref;
}

void setup() {

  Serial.begin( 9600 );
  arr_t &ret = Foo( arr );
}

void loop(){}

I'm writing an FAQ on this topic on my site ( unfinished ), however this is a basic run down.

My guess is that we are talking to cross purposes. If an array name is used as the argument in a function call, you're saying its a pointer to the first element. I agree because that is also the definition of the lvalue of the array. That is, array and &array[0] are the same thing. Once I know that, I can use the array in the function body since I know where it is stored in memory. You can see that using the following code fragment:

  int array[10];
  Serial.print("array = ");
  Serial.print((int)array);
  Serial.print("   &array[0] = ");
  Serial.println((int)&array[0]);

That's all I was trying to say: Given the proper type specifier for an array passed to the function, you get the memory address of the array (lvalue), which equates to the name of the array, which allows you to reference the array in the function.

pYro_65:
No, its not required to be a compile time constant for arrays.

void someFunc( int n ){

int arr[ n ];
 Serial.println( sizeof( arr ) / sizeof( *arr ) );
}
//...
someFunc( random( 1, 30 ) );

But in that situation, the size is known at compile time - it is know to be: sizeof(int)*n - of course it is known, otherwise it wouldn't be able to do malloc() for the array. Clearly during execution it has to do that multiplication.

pYro_65:

typedef int arr_t[ 10 ];

And that one is definitely known at compile time as sizeof(arr_t) is a type. Doing that is no different from passing an array to a function defined as:
void someFunc (int array[10]);

In fact if you were to do this the size ceases to be known:

void someOtherFunc(int* arr){
  sizeof(arr); //=sizeof(pointer)
}
arr_t array = {1,2,3,4,5,6,7,8,9,0};
someOtherFunc((int*)array_t);

n isn't known at compile time, only the sizeof int. Also its stored on the stack.

Yup, thats what I was showing.

Yup, thats exactly what I've pointed out. int*arr is not the array and never will be, it only holds the address of the first element.
I showed clearly above how to pass a pointer to an array and get the correct size using sizeof.

void Foo( arr_t *arrp ){
  
  Serial.println( sizeof( *arrp ) / sizeof( **arrp ) );
}

I showed clearly how to pass a pointer to an array and get the correct size using sizeof.

void Foo( arr_t *arrp ){

But that's not a pointer to an array; it's a pointer to a PARTICULAR SIZE of array.