How to declare an array of struct locally?

Hello all

I want to make my arrays of struct local. In a previous post (#56) I mentioned that I wanted to define some struct variables as static. Wildbull made the following reply. I do understand now, that the variables cannot be static in an array of struct.

wildbill:
Don't make any of your variables static. Static in this context in c++ means that all structs will share one copy of the static item. e.g. the led control structs will all share a single instance of pin.

Clearly, this is not your intent. The pin can certainly be const (and probably should be) but static is not what you need and it's what the compiler is complaining about, however cryptically.

If the structs shall be declared locally what is the alternative to static which will keep the value for the next itineration?

How to I declare an array of struct if part of it is needed in setup() and the other part in loop()? Do I define the variables in global scope and initialize the variables locally

struct arrayOfStruct{
const int a;
int b;
float c;
const int d;
};
setup(){
arrayofStruct Struct[2] = {
{
.a = 1,
.b = 2
},
{
.a = 4,
.b = 5
}
};
}
loop(){
arrayofStruct Struct[2] = {
{
.c = 5,
.d = 7
},
{
.c = 7,
.d = 8
}
};
}

or do I declare part of the struct in setup() and part of it in loop()

setup(){
struct arrayOfStruct{
const int a;
int b;
};
arrayofStruct Struct[2] = {
{
.a = 1,
.b = 2
},
{
.a = 4,
.b = 5
}
};
}
loop(){
struct arrayOfStruct{
float c;
const int d;
};
arrayofStruct Struct[2] = {
{
.c = 5,
.d = 7},
{
.c = 7
.d = 8}
};
}

Or what is the way to do this?

Thank you...

moses

If the structs shall be declared locally what is the alternative to static which will keep the value for the next itineration?

Nothing. Local variable lifetime is within function scope. It is destroyed at return from function. You have to initialize it at the beginning of function or better say before its first use. If there is a need to keep the value then consider to make it as global or static. Local variable can be static. Its lifetime is outside function scope but access is only from the function.

How to I declare an array of struct if part of it is needed in setup() and the other part in loop()?

Make them separate. One for setup other for loop.

Budvar10:
How to I declare an array of struct if part of it is needed in setup() and the other part in loop()? Do I define the variables in global scope and initialize the variables locally

Budvar10:
Make them separate. One for setup other for loop.

Note that if you declare the arrays of struc separately, they will each be local to where they were declared. The array declared in setup() will not be accessible in loop(). If you need access to the same array in both setup() and loop() it will need to be declared globally, and can be initialized at any time before its first use, generally in either the declaration itself, or in setup(), but can be done in loop if you are careful to only initialize it once the first time loop() executes.

Note that if you declare the arrays of struc separately, they will each be local to where they were declared.

Exactly, but if you do not use values from setup() in the loop() there is no reason to keep them and even in the same struct. It's only wasting memory space.

It's a bit hard to tell from your example because with fieldnames like a and b, it's tricky to see what you're really doing. From my quoted comment above, I see that there was a LedPin, so I suspect that you want to call pinMode on it in setup and use it in loop.

If so, you really need to make your array global. I suspect that when you declare the array, you already know the values of at least some of the fields (certainly LedPin), so initialize them when you declare it.

There are more complex ways of doing it, but get it going with a global first and come back with that code for more advice if it's not satisfactory.

Budvar10:
Local variable lifetime is within function scope. It is destroyed at return from function.

A true but incomplete statement. You can, in fact, define a static array of struct(s). As @wildbill pointed out, don't use 'static' in the struct declaration as that has a different (C++) meaning. But, you can make the entire array static and it will retain its values between calls to the function where it's defined:

struct Struct {
  const int a;
  int b;
  float c;
  const int d;
};

void setup() {
}

void loop() {
  static Struct arrayOfStruct[2] = {{2, 0, 5, 7}, {3, 0, 7, 8}}; 
}

But, as this variable is local to loop, it will only be accessible (in scope) within this function.

gfvalvo:
... you can make the entire array static and it will retain its values between calls to the function where it's defined

Thank you. That is really helpfull!

wildbill:
It's a bit hard to tell from your example because with fieldnames like a and b, it's tricky to see what you're really doing. From my quoted comment above, I see that there was a LedPin, so I suspect that you want to call pinMode on it in setup and use it in loop.

There will be several arrays of struct. Some with variables used in several functions, some with variables just used in setup() and some with variables used only in loop().

Therefore I was thinking in declaring the arrays that use variables in several functions as global. And their variables that will only be used in one function I would initialize in the function in question.

But then again I didn't know if I have an array that uses part of its variables in one setup() and some in one loop() if I should declare it global and just initialize the variables locally or if I should actually declare the array twice from a memory perspective.

But I guess part of the answer can be found in this post?

Budvar10:
Exactly, but if you do not use values from setup() in the loop() there is no reason to keep them and even in the same struct. It's only wasting memory space.

wildbill:
If so, you really need to make your array global. I suspect that when you declare the array, you already know the values of at least some of the fields (certainly LedPin), so initialize them when you declare it.

You are right. I though of declaring variables that I know as const and the once that are read from sensors as static. Therefore I would declare the whole struct static and the variables that dont need to change additionaly const?

david_2018:
...can be done in loop if you are careful to only initialize it once the first time loop() executes.

What would you mean by "once the first time"? I mean in a coding point of view and what would be the difference?

It's a little difficult to offer advice in the abstract. I suggest that you declare as many arrays as you need as globals, write some of the code you need and post it. It will help us to see what you're actually doing and it may well clarify matters for you too.

I just wanted to write the same thing. It is time for some code to discuss. Maybe some simplified one.

moserroger:
How to I declare an array of struct if part of it is needed in setup() and the other part in loop()? Do I define the variables in global scope and initialize the variables locally?

Either:

  1. define one global array and initialize it when you define it
    OR
  2. define separate local static arrays and initialize them locally.
    1:
struct arrayOfStruct {
  const int a;
  int b;
  float c;
  const int d;
} Struct[2] = {
  {1, 2, 5.0, 7},
  {4, 5, 7.0, 8}
};


void setup() {}


void loop() {}

2:

void setup() {
  static struct arrayOfSetupStruct 
  {
    const int a;
    int b;
  } Struct[2] = {
    {1, 2},
    {4, 5}
  };
}


void loop() {
  static struct arrayOfLoopStruct
  {
    float c;
    const int d;
  } Struct[2] =
  {
    {5.0, 7},
    {7.0, 8}
  };
}

Here is the array with will be present in different parts of the sketch. I realized the rest of the arrays should be either global or local:

The declaration:

struct structWATERControl
{
    const byte   resetPin; // local setup()
    const char   addr;  // local setup()
    const byte   waterFull; // local loop()
    const byte  waterEmpty; // local loop()
    const byte   waterLow; // local loop()
    const byte   waterHalf; // local loop()
    byte          waterLevel; // local loop()
    bool        waterLevelLow; // global
};

The declaration and initialization of the global variable:

struct structWATERControl
{
    bool        waterLevelLow;
};
structWATERControl grWATERControl[2] =
{
    {
        // Tank
        .waterLevelLow = true // water pump is stopped at the beginning
    },
    {
        // Pool
        .waterLevelLow = true // water pump is stopped at the beginning
    }
};

Local variables setup():

struct structWATERControl
{
    const byte   resetPin;
    const char   addr; 
};
structWATERControl grWATERControl[2] =
{
    {
        // Tank
        .resetPin = 0,
        .addr = "0x29"

    },
    {
        // Pool
        .resetPin = outputPin[SHT_LOXPool],
        .addr = "0x31"
    }
};

Local variables loop();

static struct structWATERControl
{
    const byte   waterFull;
    const byte  waterEmpty;
    const byte   waterLow;
    const byte   waterHalf;
    byte          waterLevel;
};
structWATERControl grWATERControl[2] =
{
    {
        // Tank
        .waterFull = 100,
        .waterEmpty = 220,
        .waterLow = 196, // warning when 80% of water used [0.8*(full-emty)+full]
        .waterHalf = 160, // reactivate water pump if tank is 50% full [0.5*(full-emty)+full]
        .waterLevel = 0
    },
    {
        // Pool
        .waterFull = 0, // ???
        .waterEmpty = 0, // ???
        .waterLow = 0, // ??? // warning when 80% of water used [0.8*(full-emty)+full]
        .waterHalf = 0, // ??? // reactivate water pump if tank is 50% full [0.5*(full-emty)+full]
        .waterLevel = 0
    }
};

This leads me to another question:
These arrays are for two VL53L0X time of flight sensors. There for I need to change the I2C addresse of one of them. I wanted to put it into the variable .addr. But I don't really know how to declare it and initialize it.

I thought I would make it a char:

struct structWATERControl
{
    const byte   resetPin;
    const char   addr; 
};
structWATERControl grWATERControl[2] =
{
    {
        // Tank
        .resetPin = 0,
        .addr = "0x29"

    },
    {
        // Pool
        .resetPin = outputPin[SHT_LOXPool],
        .addr = "0x31"
    }
};

This seemed to work fine with a regular arduino nano or uno. But I wanted to change to a nodemcu now. For some reason it doesn't work. It tells me "invalid conversion from const char to char".

I think I have a solution for the last part of my last post.

.addr = 0x29

Seems to work.

I am still just not sure about the rest.

Thank you.