0
\$\begingroup\$

Years ago I had a good working experience in C. Now it is rusty. Can not find the correct way to create a union with 2 elements

typedef struct {
// multiple elements
}SETTINGS;

 typedef union{
       SETTINGS PROC;
       unsigned char BYTES[sizeof PROC];
 }PROCESS_SETTINGS;

I want to use sizeof here but can not find how anymore? sizeof PROC reports an error.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Pure software questions like this one are more suitable for stackoverflow.com, where you will likely get better answers. Particularly when it comes to "language-lawyer" stuff such as this turned out to be. \$\endgroup\$
    – Lundin
    Commented Apr 4, 2017 at 9:20
  • \$\begingroup\$ @Lundin I keep that in mind. The solution you indicated in your comment on Jonk was my original idea. The solution Jonk indicated works also in the MPLABX c compiler. \$\endgroup\$
    – Decapod
    Commented Apr 5, 2017 at 6:02

3 Answers 3

5
\$\begingroup\$

The C standard says:

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type.

PROC is not an expression (that would require to go through an object of the union type first, e.g., sizeof(u->PROC)), so you have to use the type instead. And with a type, you must use parentheses:

typedef union {
    SETTINGS PROC;
    unsigned char BYTES[sizeof(SETTINGS)];
} PROCESS_SETTINGS;
\$\endgroup\$
1
  • \$\begingroup\$ Implemented already. It is ok. \$\endgroup\$
    – Decapod
    Commented Apr 3, 2017 at 7:44
4
\$\begingroup\$

There's an odd (but useful) idiom in C that is often found. And you may find it useful here.

typedef union {
    short int x;
    float y;
    unsigned char z[1];
} MyUnion;

int main( int argc, char *argv[] ) {
    MyUnion s;
    s.x = 1000;
    printf("%u %u %u\n", sizeof(MyUnion), s.z[0], s.z[1]);
    return 0;
}

Note that you don't even need to specify the actual extent of z[]. Sizing it to one meets the modern standard for syntax. Exceeding the boundary of any array at run-time is undefined behavior, but so will be using an array to index through any declaration as endian nature and padding/alignment issues are target-dependent, anyway.


The use of indices outside the given boundary are found in c-compiler code, libraries (malloc, for example, often uses [-1] as an array index), operating systems, and elsewhere. It's quite common, for example, to find in the use of:

typedef struct {
    int typecode;
    int len;
    char s[1];
} packet_t;

for network packets, object file records, etc. The actual allocation is done through malloc, though, to ensure that the proper size exists. It's not wrong and it's often done.


Since it is part of a union, it will start at the beginning. But the size of the union will be determined by the largest item and not by it.

C does require that any object have a unique address. So if the union has no other definitions inside of it, then creating an instance of the object will require space. But this is to merely meet the requirement that different instances have different addresses.

If I were simply trying to create a byte array that maps a union of object(s), I'd definitely NOT bother with a sizeof() operator, at all. I'd just write it like I did above. Saves text and saves the risk of having to edit that line if I change the name of something.

\$\endgroup\$
16
  • \$\begingroup\$ +1. Whenever I have to maintain code that makes use of unions I always feel like someone's walking over my grave. \$\endgroup\$
    – user98663
    Commented Apr 3, 2017 at 7:56
  • \$\begingroup\$ I was aware of the situation as indicated by Jonk. I needed the sizeof information for reading from and writing to eeprom in a loop. That is also the reason for creating a Union. But maybe Wossname has a better idea. \$\endgroup\$
    – Decapod
    Commented Apr 3, 2017 at 15:19
  • \$\begingroup\$ @Decapod The union is only a declaration. Not a definition. And you will need to use sizeof() as part of a run-time bit of code, no matter what else you do. So there is no change in that respect, at all. It's just a simpler declaration, is all. And better, IMHO. Of course you will need to do a run-time loop and that loop will need to know the size. But the declaration does NOT need to know the size (that's computed anyway at compile-time.) The simpler declaration is better, I think. Just an opinion, though. \$\endgroup\$
    – jonk
    Commented Apr 3, 2017 at 17:03
  • \$\begingroup\$ @Jonk. I know the union is only a declaration. I have implemented my code the way you suggest and removed also the sizeof in the declaration of the union. The only place where I use the sizeof is in the loop. \$\endgroup\$
    – Decapod
    Commented Apr 3, 2017 at 20:52
  • \$\begingroup\$ @Decapod That's how it should be, I think. The additional sizeof() there really doesn't do anything useful. Worse, if you accidentally make it a larger value than needed by the union's other members, you'd have an unintended side-effect making the union larger than needed. Of course, if you actually wanted to do that, then it might be a way of making the union always a fixed, standardized size no matter what it contained, I suppose. (There always seems to be two edges to every sword, I guess.) [I've been using C since 1978 when I first worked on the Unix v6 kernel. Long time, now.] \$\endgroup\$
    – jonk
    Commented Apr 3, 2017 at 20:59
3
\$\begingroup\$

TL;DR: Always use parenthesis after sizeof and it will work.

This is because of the peculiar syntax of the sizeof operator, C11 6.5.3:

6.5.3 Unary operators
Syntax
/--/
sizeof unary-expression
sizeof ( type-name )

As we can see it comes in two forms, one form that can be used for compile-time expressions and does not need parenthesis, and another form that can be used for types, which does need parenthesis.

Meaning:

sizeof 1;     // ok, it's an expression
sizeof int;   // syntax error, it's a type
sizeof (int); // ok

Now, as it turns out, any expression can contain a parenthesis, which is an operator with the highest precedence:

sizeof (1);   // ok, here the parenthesis is an operator of its own not part of sizeof
sizeof (int); // ok, here the parenthesis is part of the sizeof operator syntax

So one can always write a parenthesis after sizeof and it will cover both cases.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.