Understanding sizeof() question

I wanted to use sizeof() to know how many elements are in an array.

I found this snippet, but I don’t understand how it works.
Any insight on this line?

float arr = { 22.5, 31.16, 58, 102.11, 99.0 };

size_t numOfElements = sizeof arr / sizeof *arr;

void setup()
{
Serial.begin(9600);
Serial.print("The number of elements is ");
Serial.println(numOfElements);
}

sizeof array divided by a pointer to array???
How in the world does that count elements?

void loop()
{
;
}

For an array, sizeof *arr" is basically the same as "sizeof arr[0]."

I wanted to use sizeof() to know how many elements are in an array.

size_t numOfElements = sizeof arr / sizeof  *arr;

sizeof array divided by a pointer to array???
How in the world does that count elements?

The pointer to arr is a pointer to the first element of the array. You are dividing the total number of bytes in the array by the number of bytes in the first element of the array. Floats are four bytes, so the complete array size is 20 bytes. The size of a single element is 4 bytes. Division gives you the number of elements.

You may find it more understandable to use arr[0] instead of *arr to get the size of a single element.

void setup() {
  float arr[] = { 22.5, 31.16, 58, 102.11, 99.0 };
  Serial.begin(115200);
  size_t numOfElements = sizeof arr / sizeof  * arr;
  Serial.print("The number of elements is ");
  Serial.println(numOfElements);
  Serial.print("The total number of bytes is ");
  Serial.println(sizeof arr);
  Serial.print("The number of bytes in the first element using pointer is ");
  Serial.println(sizeof * arr);
  Serial.print("The number of bytes in the first element using element index is ");
  Serial.println(sizeof arr[0]);

}

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

}

1. In this array: float arr[] = { 22.5, 31.16, 58.0, 102.11, 99.0 };, we have 5 members (aka elements) where each member is a data of type float.

2. When we declare a variable like this (with float keyword): float x = 31.16;, the Compiler saves four bytes data (against the value of the variable x) into four consecutive memory locations as per binary32 convention of IEEE-754 standard. In this case (for the value of 31.16), the Compiler saves: 41 F9 47 AE (41 is the Most Significant Byte). In case, the OP wishes to know I have given below a sketch that obtains the binary32 formatted value of a float variable.

void setup() 
{
  Serial.begin(9600);
  float x = 31.16;
  unsigned long *p;
  p = (unsigned long*) &x;
  unsigned long m = *p;
  Serial.println(m, HEX); //Shows: 41F947AE
  while(1);
}

3. From Step-2, we can conclude that the array of Step-1 contains 5x4 = 20 byte data which can be known by executing these code:

byte n = sizeof(arr)
Serial.print(n); //shows 20

4. Having known before hand that the float type data is represented by 4-byte value, we could easily calculate the element/member size of the array of Step-1 by executing this instruction:

byte numberOfElements = sizeof(arr)/4;   //sizeof(arr)(=size arr) returns number of bytes in arr
Serial.print(numberOfElements); // shows 5

OR

size_t numberOfElements = sizeof(arr)/4;
Serial.print(numberOfElements); // shows 5

size_t is an unsigned integer data type which is defined in various header files like: <stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, < time .h>, <wchar.h> chevron_right. It’s a type which is used to represent sizes of objects in bytes, hence it can be returned by the sizeof operator.

However, in the sketch of the OP, we have this code:

size_t numberOfElement = sizeof arr/sizeof *arr;
==> byte numberOfElements = sizeof(arr) / sizeof(*arr);

5. Let us see, how sizeof(*arr); does return 4.
Let us remember the fact that the array name (here arr) always points to the first element/member (arr[0]) of the array. Therefore (as has been shown in the previous posts),

Serial.println(sizeof(*arr));   //shows: 4
==> Serial.println(sizeof(arr[0]));         //shows: 4

BTW: There is an advantage of using sizeof(*arr); instead of using the fixed value 4. We have another keyword double which is used to declare/define 64-bit (double precision) floating point number where each element of the array is 8-byte long. So, if we use sizeof(*arr);, there is no need to look for 4 in the sketch (there could be many 4s; it is which 4 that we are looking for) and change it to 8; instead, we just change the data type form float to double. The codes remain dynamic. For example (testable on Arduino DUE):

double arr[] = { 22.5, 31.16, 58.0, 102.11, 99.0 };
byte numberOfElement = sizeof(arr)/sizeof(*arr);
Serial.println(numberOfElements); //shows: 5

Personally I think that using

sizeof(arr) / sizeof(arr[0]);

makes what the code is doing much clearer and I would certainly never use a constant for the divisor

UKHeliBob: Personally I think that using

sizeof(arr) / sizeof(arr[0]);

makes what the code is doing much clearer and I would certainly never use a constant for the divisor

To me, the following one is a bit cryptic:

sizeof(arr) / sizeof(*arr);

Better to be cryptic than shortsighted, IMO.

AWOL: Better to be cryptic than shortsighted, IMO.

Then, I would look for some kind of beauty in the usage of sizeof(*arr)?

'beauty is truth, truth beauty,' – that is all / Ye know on earth, and all ye need to know"

Keats

Love is heaven, heaven is love.

anonymous

"L'enfer, c'est les autres"

J-P Sartre :D

ভালোবাসা হল স্বর্গ, স্বর্গই হল ভালোবাসা।

...

GolamMostafa: bla bla bla

Isn't that a long circumvolution to explain that [url=https://en.cppreference.com/w/cpp/language/sizeof]sizeof[/url] returns the number of bytes that would be used to represent the argument and that the name of an array gets implicitly converted into a pointer to the first element of the array

PS/ You missed an opportunity to explain the type subtlety between the array name and a pointer to the first element of the array.:)

PS/this

  unsigned long *p;
  p = (unsigned long*) &x;
  unsigned long m = *p;
  Serial.println(m, HEX); //Shows: 41F947AE

seems a long way to write   Serial.print(*((unsigned long*) &x), HEX); //Shows: 41F947AEisn't it ?

J-M-L: isn't it ?

Certainly, the two versions have of their own merits and demerits.

My version is the Prose and Your version is the Poetry.

It is upto the Forum Members to judge which one is of immediate need in this Arduino Forum where almost all novice members do 'copy and paste' of programs.

GolamMostafa: It is upto the Forum Members to judge which one is of immediate need in this Arduino Forum

they are probably both useless in the context of the original question.... :)

J-M-L: they are probably both useless in the context of the original question.... :)

You have delivered relaxations to all parties for which there is a K+.

I prefer:

 const unsigned ArraySize = sizeof Array / sizeof Array[0];

Leaving out the unnecessary parentheses may teach someone that 'sizeof' is an operator, not a function. :)

GolamMostafa:
My version is the Prose and
Your version is the Poetry.

And both are UB :wink:

johnwasser:
I prefer:

 const unsigned ArraySize = sizeof Array / sizeof Array[0];

Leaving out the unnecessary parentheses may teach someone that ‘sizeof’ is an operator, not a function. :slight_smile:

First K+ for the quoted post and then the next queries –

1. Advantage of the usage of ‘size_t’ over ‘unsigned int’.

2. Referring to the following post:

AWOL:
Better to be cryptic than shortsighted, IMO.

In what sense, the declaration sizeof([arr[0]) is ‘shortsighted’ in relation to this declaration: sizeof(*arr)?

3. Then should we write ‘for(int i=0; i<4; i++)’ as ‘for int i=0; i<4; i++’ to convey that for is not a function.

4. Then for what reason, the Compiler in never confused to accept sizeof(arr) or for() as function.

Then for what reason, the Compiler in never confused to accept sizeof(arr) or for() as function.

RTFM...it’s in the language spec, they are not functions

For sizeof: It’s all in the definition of the operator and understanding of what parenthesis do for an expression

—— Queries size of the object or type. Used when actual size of the object must be known. Syntax sizeof( type ) (1) * *sizeof expression (2) * *Both versions return a constant of type std::size_t. Explanation 1) Returns size in bytes of the object representation of type. 2) Returns size in bytes of the object representation of the type that would be returned by expression, if evaluated. ——-

Idem for for loop ——— for loop * C++ C++ language Statements* Executes init-statement once, then executes statement and iteration_expression repeatedly, until the value of condition becomes false. The test takes place before each iteration. Syntax * formal syntax: * attr(optional) for ( init-statement condition(optional) ; iteration_expression(optional) ) statement * * informal syntax: * *attr(optional) for ( declaration-or-expression(optional) ; declaration-or-expression(optional) ; expression(optional) ) statement ————-