Understanding structures and pointers

Hi, as I am very new to structures and pointers there are some open questions. I declared some structures and now I try to get the right variable-usage. I destilled my code down, to make it more obvious where my question ist targeting.

typedef struct {
  time_t time;
  byte color[5];
} PROGMEM tSchedule;

typedef struct {
  byte anyVar1;
  tSchedule schedule[];
} PROGMEM tProgram;

typedef struct {
  byte anyVar2;
  tProgram* program; // this should be a pointer
} PROGMEM tChanels;

tProgram aPrograms[7];
tChanels aChanels[9];

So besides some other variables there is an array of tSchedule in every tProgram which should hold the values. All tPrograms should be held in an array aPrograms.

In the Structure tChanels there should be a pointer to a tProgram (an element of aPrograms).

Now the questions are, if my structure-declaration does what I like it to do and: How do I address a color from a tChanel? Is that the right way:

aChanels[0].program -> schedule[0].color[0] = 255;

How do I pass the pointer? Is that correct?

tProgram* pointer = aChanels[0].program; // a pointer itself
tProgram* pointer2 = aPrograms[0]; // no pointer itself

Is there an easy way to cycle through all aChanles[].program without calling a tProgram twice? I would generate an additional tProgram-array holding all pointers which are held in aChanels[], but maybe there is a smarter solution using some query or such.

Thanks, Kevin

Now the questions are, if my structure-declaration does what I like it to do

Not entirely.

  tSchedule schedule[];

The size of the array must be defined somehow. Either you explicitly define it, or you supply initializers and the compiler counts them to determine the size. Dynamic sizing is not possible this way.

Otherwise, yes.

How do I address a color from a tChanel? Is that the right way

Yes.

How do I pass the pointer?

Not sure that the question is relevant in light of the code shown, since there is no pointer passing involved in that code. Pointer assignments, yes. Passing, no.

tProgram* pointer = aChanels[0].program; // a pointer itself

Haven't seen an assignment for aChanels[0].program, so now there are two NULL pointers.

Is there an easy way to cycle through all aChanles[].program without calling a tProgram twice?

What do you mean by "cycle through"? You have an array of tChanels objects, each of which has a pointer to a tProgram object. If you use a for loop, you can loop through the tChanels objects, and do something with the tProgram object that each tChanel object points to. You aren't calling anything, though, since the tProgram object isn't a pointer to a function.

I would generate an additional tProgram-array holding all pointers which are held in aChanels[]

Why? I don't see the benefit.

Hi,

Haven’t seen an assignment for aChanels[0].program, so now there are two NULL pointers

skipped some code where aPrograms and aChanels get filled. But as they hold no dynamic values I thought this would not be neccessary to write it down. I should have mentioned that there is such a filling though. sorry.
I try to rephrase my question:

aChanels[0].program = aPrograms[0];
tProgram* pointer = aChanels[0].program; // a pointer itself
tProgram* pointer2 = aPrograms[0]; // no pointer itself

Is this code valid? Am I right, that now pointer = pointer2 = aChanels[0].program are pointing to aProgram[0]?

What do you mean by “cycle through”? You have an array of tChanels objects, each of which has a pointer to a tProgram object. If you use a for loop, you can loop through the tChanels objects, and do something with the tProgram object that each tChanel object points to. You aren’t calling anything, though, since the tProgram object isn’t a pointer to a function.

I do not really want to call a tProgram, but call a function with tProgram as a parameter. Lets say:

void add_any(tProgram* prg)
{
  prg -> anyVar1++;
}

void loop()
{
  for (p = 0; p < 7; p++) {
    add_any(Programs[p].program);
  }
}

If now two Chanels have a pointer to the same tProgram anyVar1 would increment by two instead of one like I want it to. I see now two ways of dealing with it: I could check, if I have already incremented in this loop or I could first generate an array without any redundancy.
But I hoped there might me a third way. Some query like all aChanels[].program in aChanels[] - perhaps?

Greets,
Kevin

Is this code valid? Am I right, that now pointer = pointer2 = aChanels[0].program are pointing to aProgram[0]?

Yes and yes.

I do not really want to call a tProgram, but call a function with tProgram as a parameter.

Well, there are some problems with that code. Programs? Where did that come from? Did you mean aPrograms?

Anyway, the structure of the code is right, and you are passing arguments correctly, so the code should work.

If now two Chanels have a pointer to the same tProgram anyVar1 would increment by two instead of one like I want it to.

True, because there is a shared instance of tProgram. If you don't want anyVar1 to increment twice, you can't share instances of tProgram. Or, if you do, you must keep track of whether you've seen/dealt with that instance before.

But I hoped there might me a third way. Some query like all aChanels[].program in aChanels[] - perhaps?

Well, there will be when you get done writing it.

Did you mean aPrograms?

right. forgot a

Well, there will be when you get done writing it.

If there are no dynamic querys build-in it will get some lines to write such. It would be easier to have an array which automatically keeps a list of all used pointers. For now I will fill this additional array during runtime. In the future (when I am more skilled) I plan to put it in the accessor-methods (get/set).

Thanks a lot for your help.

It would be easier to have an array which automatically keeps a list of all used pointers.

It would, I guess.

In the future (when I am more skilled) I plan to put it in the accessor-methods (get/set).

From a hair-retention point of view, you might want to do this now. It is no more difficult to do in the set method, assuming you create one, than it is to do somewhere else. Doing it in the set method guarantees that it IS done.

first I thought about doing it in the set-method from aChanels.program, but wouldn’t it be nicer to do it in a get-method from a new variable, so it gets calculated every time?
Although my skills soon come to an end I thought about something like:

public tPrograms* usedProgs[7] {
  get{
    cnt_usedProgs = 0;
    for (byte ch = 0, ch < 9, ch++) {
      boolean isInIt = false;
      for (byte uPg = 0, pg < cnt_usedProgs, uPg++) {
        isInIt(aChanels[ch].program == aPrograms[pg])?true:false;
      }
      if (!isInIt) {
        _usedProgs[cnt_usedProgs] = aChanels[ch].program;
        cnt_usedProgs++;
      }
    }
    return _usedProgs[];
  }
  set{}
}

I think you've been doing C# too long...

may be. Couldn’t I do something like that in C?

How do I create a set/ get-method? do you have any links? google just gives me c#.

C# does a lot to link get and set methods to variables. C/C++ does not. You can have a function that gets data and a function that sets data. But, you have to tie the get method to the variable that it returns, and tie the set method to the variable(s) that it sets.

sounds difficult. Is there an example anywhere?

Back to the structure-thing. I tried to use structures in an existing code and had problems. If I he compiler would say ‘tProgram’ was not declared in this scope.
Why could this be? I shortened the code as much a lot to identify the problem, but I still do not see it.

#include <avr/pgmspace.h>
#include <Streaming.h>

#define cnt_chanels 1
#define cnt_usedPrograms 1
#define cnt_programs 1
#define cnt_colors 5
#define gamma_resolution 255

long gamma[cnt_colors][gamma_resolution];            // index is the gamma-corrected value of its value(/1000)         
unsigned long last_recalculation;                    // stores the last complete recalculation to deal with milllis()-rollover

typedef struct {                                 //STRUCT: Single Step
  time_t   time;                                 //timestamp of each single step
  byte     color[5];                             //color-values of each single step
} PROGMEM tStep;

typedef struct {                                 //STRUCT: Single Program
 // byte        program;                           //Program-Number (not neccessarry)
  byte        act_realColor[cnt_colors];         //at-the-time-color for this program
 // boolean     active;                            //is that program used in a Chanel (not neccessary)
  int         act_step;                          //active timeStep of this program
  int         cnt_steps;                         //number of timeSteps in
  tStep       steps[];                           
} PROGMEM tProgram;

typedef struct {
  byte            pin;
  byte            realColor;
  int             inc_delay;
  int             inc_colinc;
  unsigned long   lst_timer;
  byte            inc_repeat; 
} PROGMEM tDevice;

typedef struct {
  tProgram program;  
  tDevice devices[cnt_colors];
  byte   program_ID;
  byte   act_realColor;
  byte   act_visColor;
} PROGMEM tChanel;

tChanel Chanels[cnt_chanels];
tProgram Programs[cnt_programs];
tProgram usedPrograms[cnt_usedPrograms];
 
// calculates the following step in the prg_times for a given program
int nextstep(tProgram* prg, int startstep)
{
  int rt = (startstep < prg->cnt_steps)?startstep++:0;
  return rt;
}

void setup()
{}

void loop()
{}

Back to the structure-thing. I tried to use structures in an existing code and had problems. If I he compiler would say 'tProgram' was not declared in this scope. Why could this be?

That PROGMEM directive is causing the problem. It is not the structure that you want defined in PROGMEM. It is the instances of the structure that should be in PROGMEM.

Thanks, I copied the PROGMEM-storing without really understanding it. maybe I should look into that a little deeper...

But even if I delete "PROGMEM" completely from this code the compiler still says 'tProgram' was not declared in this scope.

Interesting situation. I was able to get the code you posted to compile, after making some changes.

#define cnt_chanels 1
#define cnt_usedPrograms 1
#define cnt_programs 1
#define cnt_colors 5
#define gamma_resolution 255

struct Step
{                                 //STRUCT: Single Step
  long   time;                                 //timestamp of each single step
  byte     color[5];                             //color-values of each single step
};
typedef struct Step tStep;

struct Program
{                                 //STRUCT: Single Program
  byte        act_realColor[cnt_colors];         //at-the-time-color for this program
  int         act_step;                          //active timeStep of this program
  int         cnt_steps;                         //number of timeSteps in
  tStep       steps[];                           
};
typedef struct Program tProgram;

struct Device
{
  byte            pin;
  byte            realColor;
  int             inc_delay;
  int             inc_colinc;
  unsigned long   lst_timer;
  byte            inc_repeat; 
};
typedef struct Device tDevice;

struct Chanel
{
  tProgram program;  
  tDevice devices[cnt_colors];
  byte   program_ID;
  byte   act_realColor;
  byte   act_visColor;
};
typedef struct Chanel tChanel;

tChanel Chanels[cnt_chanels];
tProgram Programs[cnt_programs];
tProgram usedPrograms[cnt_usedPrograms];
 
// calculates the following step in the prg_times for a given program
int nextstep(struct Program *prg, int startstep)
{
  int rt = (startstep < prg->cnt_steps)?startstep++:0;
  return rt;
}

void setup()
{}

void loop()
{}

The nextstep function declaration was the cause of the compiler error. Explicitly defining that the function takes an argument of “pointer to struct Program” solved the problem.

I expected that the typedef statement would have handled that.

Hi, thanks for putting so much effort in it and I am glad that it sould work now, but could you please explain, what your changes do exactly? Because I do not see the diffeerence.

Is it just a difference in the writing or is there a logica difference if you call typedef after declaring the structure? Also, can you put in short what is the difference between tProgram* prg and struct Program *prg? I now could use your code, but understanding it could be helpful :)

aChanels[0].program -> schedule[0].color[0] = 255;

This is how you do it if everything is in RAM. I don't know what extra hoops you might have to jump through for data structures in PROGMEM.

I don't know what extra hoops you might have to jump through for data structures in PROGMEM

Well, that assignment is out of question for a start!

In your code, you are declaring an anonymous structure, and typedef'ing that to tProgram, for instance.

In my code, I am declaring a named structure, and typedef'ing that to tProgram. This requires a separate typedef statement.

The typedef statement says that you want to be able to refer to one name (struct Program, for instance) using another name (tProgram).

In the function body, apparently you can't use the typedef name.

I created a function prototype, and in that I was able to use the typedef name, but not in the function implementation.

This is how you do it if everything is in RAM. I don't know what extra hoops you might have to jump through for data structures in PROGMEM.

I never calculated it, but as you can see a tStep contains a long and a 5byte-array, which makes 9 bytes. Lets take 50 of them in a program. That makes 460 bytes per program. If I have 10 programs it is more than 4kB. I might not need all the 4 kB and as I have a mega it should be enough... I just thought that this would not make the code much more complex, but would solve the problem of always keeping SRAM in mind...

The typedef statement says that you want to be able to refer to one name (struct Program, for instance) using another name (tProgram). In the function body, apparently you can't use the typedef name.

So there should not be a difference between those two expressions, but there is ! ?

Now I would like to call the function like:

nextstep(Programs[0].program, 0);

But this would not work, because this is a different type. ? I really did not get completely what this typedef does. It is not a simple substitute like #define, but it is not a pointer either... ?

So there should not be a difference between those two expressions, but there is ! ?

Alas, this appears to be a true statement.

But this would not work, because this is a different type. ?

This is true. But, not for the reason you are thinking.

Programs[0] is an element in the Programs array, which is of type tProgram, which is typedef'ed to equal struct Program. The Program struct does not contain a member called program.

Now

nextstep(Chanels[0].program, 0);

would compile and link, and would work.

Chanels[0] is an element in the Chanels array, which is of type tChanel, which is typedef'ed to equal struct Chanel. In theory, you could use "tChanel" or "struct Chanel" interchangeably. On the Arduino, this does not appear to be the case in practice.