-1

I've got a question regarding the behavior of structs in C.

Take the given structs as an example:

typedef struct {
    char num1;
    char num2;
} structa;

typedef struct {
    structa innerstruct;
    char num3;
    char num4;
} structb;

I assume since structb contains a structa field, rather than a pointer to one, that structb would be laid out in memory as such:

struct {
    char num1; //innerstruct num1
    char num2; //innerstruct num2
    char num3; //num3
    char num4; //num4
};

If so, can you access each field like this?

*(((char *)&structbvar) + 0) = 1; //innerstruct num1
*(((char *)&structbvar) + 1) = 2; //innerstruct num2
*(((char *)&structbvar) + 2) = 3; //num3
*(((char *)&structbvar) + 3) = 4; //num4

or access them like this?

((structa)structbvar).num1 = 1; //innerstruct num1
((structa)structbvar).num2 = 2; //innerstruct num2

Regardless of if that's bad practice or not.

Also is it reliable/portable?


Edit, as pointed out by Matt McNabb you can't directly cast structs so it should be:

((structa *)&structbvar)->num1 = 1; //innerstruct num1
((structa *)&structbvar)->num2 = 2; //innerstruct num2
NotVeryMoe
  • 544
  • 5
  • 9
  • 3
    You can try it, and you'll usually get away with it, but it isn't defined that it must work. Don't do it; it makes your code obscure beyond reason. – Jonathan Leffler Apr 05 '15 at 04:28
  • I've tired it on my system, and it appeared to work. Thing is though, I'm not sure if it's portable and if not, why not. I'm not worried about if it's bad practice though, I already know that it definitely is and I won't be writing code like this but it's good to know how C ticks. – NotVeryMoe Apr 05 '15 at 04:28
  • 1
    you gotta make sure you're structs are aligned – Ryan Apr 05 '15 at 04:57
  • Yeah, I'm actually reading about that right now, since Matt McNabb pointed out padding between variables. I had no idea about it, but it makes a lot of sense! – NotVeryMoe Apr 05 '15 at 05:02
  • 1
    You can use `offsetof` rather than 1, 2, 3, 4, and if you're careful end up with something portable. – Paul Hankin Apr 05 '15 at 05:26

2 Answers2

2

The *(char *) version is well-defined although you will need to check for padding after the end of innerstruct and before num3.

(structa)structbvar is ill-formed. I think *(structa *)&structbvar would be OK, because of the rule about structs with common initial sequence (although I'll double-check that and update my answer). However that is a bit pointless as you could just write structbvar.innerstruct .

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Ah your correct about the (structa)structbvar, I totally forgot that you can't directly cast one struct another, thanks! I'll chuck in an edit note bellow the offending lines. Thanks for the note about padding too. I hadn't thought of that. Yeah, I understand it's a bit pointless, but this is more of a question about how c ticks than anything else. – NotVeryMoe Apr 05 '15 at 04:40
2

If you use pointer to force access a struct field, the reliability of your code depends on the alignment of your memory. So it's not recommended, unless you force the memory to align as you expected.

Another way to access the field without calling the nested struct name, is using the Anonymous Struct with union. It's C99 Standard and of course portable and reliable, except your compiler doesn't support C99.

You can define a union with Anonymous Strct like this:

typedef union {
    uint32_t wWord;
    struct {
        uint16_t hwLow;
        uint16_t hwHigh;
    };
} test_t;

Then access each field like this:

test_t tTest;
tTest.wWord = 0x00FF0000;

Also:

tTest.hwHigh = 0x00FF;
kevin wu
  • 72
  • 4