1

I've just started to use typedefs for the first time, and I haven't used structures a whole lot either, though I understand them very well.

I've created a type call Max7219_t as follows:

typedef struct {
    uint16_t digit1,
    uint16_t digit2,
    uint16_t digit3,
    uint16_t digit4,
    uint16_t digit5,
    uint16_t digit6
    uint16_t digit7,
    uint16_t digit8,
    uint16_t intensity,
    uint16_t _shutdown,
    uint16_t scanl,
    uint16_t testmode,
    uint16_t decodetype } Max7219_t;

Later in the code, after I've declared a variable of this type and added values, I need to transfer each byte in the struct one at a time, ideally in a for loop.

Is there any way to do this besides referring to each member directly (i.e., *display->digit1, then *display->digit2 etcetera)? Essentially, is there a way to enumerate each entry the same way you'd do so with an array?

I don't want to just convert the whole thing to an array, because I almost certainly will add different types to this and other structures later on during development.

I feel like I'm missing something obvious here. I was thinking I could just increment a pointer, but because structure members aren't all the same size, I have a feeling this doesn't work like it does with pointers to arrays. Plus, when iterating through a for loop, I'm not sure if something like for(q = 0; q != sizeof(Max7219_t); q++) would work either.

Any help is much appreciated!

J0e3gan
  • 8,740
  • 10
  • 53
  • 80
Aurelius
  • 414
  • 4
  • 16
  • `because structure members aren't all the same size` -- in your case? – Sourav Ghosh Nov 01 '14 at 21:49
  • Do you intend the members of this structure to be one byte in size or two? `uint16_t` is two bytes. –  Nov 01 '14 at 21:50
  • 1
    Compilers sometimes "pack" structs differently. – Evan Carslake Nov 01 '14 at 21:52
  • Your types are the same, why not have an array inside the struct? – 2501 Nov 01 '14 at 22:17
  • Why do you need to transfer `each byte in the struct one at a time`? Most people would move the data all at once. Understanding this might help us give you a better solution. – Degustaf Nov 01 '14 at 22:35
  • Sorry for the vague wording, by transfer I meant over SPI, to a chip. I believe the answer below that I've picked is the best solution in my case. – Aurelius Nov 01 '14 at 22:49

5 Answers5

3

If you need to access raw memory that is "hidden" behind the struct, there are a couple of methods:

  • memcpy()
  • for loop with type-casted pointer

The last approach seems to be the closest to what you want, so maybe sth like this:

Max7219_t yourStructure;
// ...

const uint8_t* const pointer = (const uint8_t*)&yourStructure;
for (size_t i = 0; i < sizeof(yourStructure); ++i)
{
    uint8_t byte = pointer[i];
    ...
}

Your question is not entirely clear...

Freddie Chopin
  • 8,440
  • 2
  • 28
  • 58
1

Since all the elements in the struct are the same size, theoretically you can declare a pointer to uint16_t that points to the first element in the structure like this:

Max7219_t my_structure;
uint16_t *p = (uint16_t *)&my_structure;
for (int i = 0; i < ELEMENTS_IN_STRUCTURE; i++, p++) {
    *p = some_value;
}

However, I would not recommend doing this at all since it wouldn't work if you added some other variable type to the struct, and it completely defeats the purpose of structures and types.

What I would do instead, is to write a function that fills the values as needed.

void init_max_77219(Max7219_t *my_structure) {
    my_strcture->digit1 = ...;
}

And then just call it whenever you need to initialize the structure.

For this particular scenario (the struct acting as a bag of uint16_t), you can stick to an array if you want, and then store variables of different types elsewhere.

Nina Satragno
  • 561
  • 3
  • 8
  • This is kind of what I was originally thinking, but it won't work for my particular problem. Thanks for the help though! – Aurelius Nov 01 '14 at 22:48
1

Since your types are the same, create an array of those types:

typedef struct
{
     uint16_t mode[13] ;

}  Max7219_t ;

You iterate trough it like trough any other array.

 Max7219_t var = { 0 } ;

 for( size_t i = 0 ; i < 13 ; i++ )
 { 
     var.mode[i] = 12345 ;
 }

This method will not allow you to use names for your elements: digit1, digit2 ...

You can get those by using enumerations, which correspond to the correct element in the array.

enum
{
    digit1 = 0 ,
    digit2 = 1 ,
    digit3 = 2 ,
    //... and so on
} ;
2501
  • 25,460
  • 4
  • 47
  • 87
1

If you want to:

Later in the code, after I've declared a variable of this type and added values, I need to transfer each byte in the struct one at a time, ideally in a for loop.

You can just use memcpy() to copy raw data or if you want to access structure "byte by byte" you can:

char *ptr = (char*)&Max7219_t_instance;
for(int i = 0; i < sizeof(Max7219_t) ++i){
    // Work with bytes
}

Note that you don't change anything to array, you cast create pointer that will address the same data as different type.

If you want to iterate trough struct members (and they are not the same type... otherwise use 2501s answer) you're going to need reflection. There are ways how to employ reflection in C, but I would be worried about performance impact.

Just imagine situation:

??? value = display->digit[x]; // This can be char/short/int/long...

You could solve this by using long on left side, but with floats and strings...

Oh well... You could parse .pdb for MS compiler or debug section for gcc (with -g turned on), but it's far away from portable C implementation.

But when you need to work with integer types (or you can work with raw binary data) you can create array like this:

int Max7219_t_structure_members[] = {sizeof(uint16_t), sizeof(uint16_t) ..., 0 };

Which would take size of each structure member and

sum(Max7219_t_structure_members) == sizeof(Max7219_t)

And for sake of argument let's imagine that you need to work just with types up to sizeof(long) bytes large.

int offset = 0;
for(int i; Max7219_t_structure_members[i]; ++i){
    long tmp = 0;
    // This line will work only on little-endian machines
    memcpy(&tmp, ((char*)insntance)+offset, Max7219_t_structure_members[i])

    // Do your work

    offset += Max7219_t_structure_members[i];
}

But you would have to maintain the struct manually. I don't know about any macros (at least in gcc) that would help you with automated version.

Community
  • 1
  • 1
Vyktor
  • 20,559
  • 6
  • 64
  • 96
  • This is another interesting answer, and one that would definitely work, especially if deterministic behaviour was needed. I'm sorry my wording was vague, by transfer I meant over SPI. – Aurelius Nov 01 '14 at 22:52
-1

A use case for unions:

union variable {
    Max7219_t yourStructure;
    uint16_t array[13];
);
mfro
  • 3,286
  • 1
  • 19
  • 28