How to get the sizeof array of structs

I’m trying to make a use of a relay module, I want a clear way to configure and control my relay module I’ve defined the struct and filled it with some info about id, pin number, title, and relay state I can’t loop with for in the array of controls, because I can’t get the sizeof the array right

typedef struct{
int id;
int pin;
String title;
bool state;
} SwitchControl;

SwitchControl controls[2] = {
{1, 22, "Switch 01", 1},
{2, 23, "Switch 02", 0},
};

for(int i=0; i<sizeof(controls) - 1; i++){
}

thanks

sizeof (X) / sizeof (X[0]) gives the number of elements in array X

I’m not sure what the “-1” is for.

Awesome !
Thanks a lot it works !

Try to use range-based for loops whenever possible, it avoids index errors like this:

for (SwitchControl &control : controls) { // for each control in the `controls` array
  // do something with `control`
}

The ampersand means that control is a reference of the array element. If you leave it out, control will be a copy of the array element.

If you need the index, I like this len helper. It’s harder to get wrong than the sizeof approach:

// Get the number of elements in a given array
template <class T, size_t N> constexpr size_t len(const T (&)[N]) { return N; }

for (size_t i; i < len(controls); ++i) {
  // do something with `controls[i]`
}

Pieter

PieterP:
If you need the index, I like this len helper. It’s harder to get wrong than the sizeof approach:

template <class T, size_t N> constexpr size_t len(const T (&)[N]) { return N; }

Yeah.
Right.

TheMemberFormerlyKnownAsAWOL:
Yeah.
Right.

You copy/paste it once, and then the usage is perfectly intuitive, just like the Python len(...) function, for instance.
The len function is much harder to use incorrectly. You cannot forget to divide by sizeof(X[0]), which is a very common mistake. Even if you make a mistake in the function definition, the compiler will yell at you, unlike sizeof, which will just cause out of bounds array accesses at run-time if you get it wrong.

I never claimed it was easy to come up with such a function.

I’ve never known the preprocessor to “forget to divide by sizeof(X[0])”

YMMV

TheMemberFormerlyKnownAsAWOL:
I've never known the preprocessor to "forget to divide by sizeof(X[0])"

:smiley: :smiley:

TheMemberFormerlyKnownAsAWOL:
sizeof (X) / sizeof (X[0]) gives the number of elements in array X

The sizeof trick works for arrays,but is not type safe. The compiler will quite happily use it if applied to a pointer.

"If you apply it to pointers, that's a bug! Don't do that!". Well sure, but why not use a solution that actually prevents the problem, instead of leaving you to find it yourself?

It's all that's available when using 'C', but in C++ there are many different better solutions that rely on templates to provide the same answer but in a type-safe manner.

MHotchin:
The sizeof trick works for arrays,but is not type safe. The compiler will quite happily use it if applied to a pointer.

Nonsense. It works just fine for a pointer. If ask for sizeof() a pointer, it will give you 2 (AVR) or 4 (ARM). And, "sizeof (X) / sizeof (X[0]) " works wonderfully on an array of pointers to give you the number of elements in the pointer array.

It’s funny - I’ve spent a long time working the system side of Linux (the side that doesn’t have a safety net).

I’ve often wondered why Torvalds has never embraced all the manifest advantages of C++.

TheMemberFormerlyKnownAsAWOL:
I’ve often wondered why Torvalds has never embraced all the manifest advantages of C++.

I very much like the OOP aspect of C++. I just don’t need a template<…> class for everything under the sun to protect me from myself.

gfvalvo:
Nonsense. It works just fine for a pointer. If ask for sizeof() a pointer, it will give you 2 (AVR) or 4 (ARM). And, "sizeof (X) / sizeof (X[0]) " works wonderfully on an array of pointers to give you the number of elements in the pointer array.

You mis-understand the problem.

char c;
char *p = &c;

size_t funny_size = sizeof(p)/sizeof(p[0]);

This is valid code, but semantic nonsense. The sizeof() trick for arrays will happily accept boring pointers, and provide results that are 'correct' but not useful.

Well, I guess there comes a time that you need to know what you're doing. Give an imbecile a loaded gun and bad things will happen.

TheMemberFormerlyKnownAsAWOL:
I’ve never known the preprocessor to “forget to divide by sizeof(X[0])”

No one is talking about the preprocessor. Clearly, programmers regularly forget to divide by sizeof(X[0]), OP isn’t the first and certainly won’t be the last.

A programming language and its guidelines should protect programmers against common mistakes. By using low-level constructs like sizeof to get the size of an array, you leave room for mistakes like this.
I have no doubt that you have enough experience to avoid them, but 99% of Arduino users don’t have that experience, and some will eventually make the same mistake.

gfvalvo:
Well, I guess there comes a time that you need to know what you’re doing. Give an imbecile a loaded gun and bad things will happen.

I do not agree with this argument. If the past couple of decades of C programming have taught us anything, it’s that even the most experienced programmers who write entire operating systems or critical encryption libraries sometimes make silly and preventable mistakes.

Besides, exactly 100% of programmers had no idea what they were doing at some point in their career. Getting the size of an array is something that many beginners will need early on, so you cannot just expect them to know all intricacies of the language. Instead, I propose we point them to methods that are hard to misuse, the compiler should at least throw a warning or an error when they make a silly mistake.

The point is not to ban all inexperienced programmers because they might shoot themselves in the foot, but to give them safer alternatives instead of handing them a gun when they’re not ready for it.

Final array length example:

int array[] = {1, 2, 3};

void function1() {
  size_t size = sizeof(array) / sizeof(array[0]);
  for (size_t i = 0; i < size; ++i)
    Serial.println(array[i]);
}

void function2(int array[]) {
  size_t size = sizeof(array) / sizeof(array[0]);
  for (size_t i = 0; i < size; ++i)
    Serial.println(array[i]);
}

void setup() {
  Serial.begin(115200);

  function1();
  function2(array);
}

void loop() {}

function1 works fine, function2 doesn’t. It compiles just fine, though. Good luck debugging this when you’re just learning about arrays and loops!

They’ll eventually have to learn what’s going on, but until they reach that point, let’s give them tools and methods that are hard to misuse because they result in compiler errors, for example. When using the len function I posted earlier, you get an error that you can’t take the len of a pointer, instead of “inexplicable” behavior at run-time:

In function 'void function2(int*)':
error: no matching function for call to 'len(int*&)'
   len(array)
            ^

Is using a template an ideal solution? Of course not, far from it, but it’s the best we have in C++ at the moment, and if it prevents bugs and frustration, it should be preferred to error-prone alternatives like sizeof, in my opinion.