0

I wasn't exactly exactly sure what title I should use for this question, but I'll try to clarify through an explanation:

So the long and short of it is that I want a functions argument to accept both an array AND an integer for the same argument. Not too difficult, just use a pointer right? So I have this:

#define BIT0 (unsigned char){0b00000001}

void    foo (unsigned char *ptr_bar)
{
    printf("%x", *ptr_bar);
}

void    main    (void)
{
    foo(&BIT0);
}

Works all fine and dandy for a regular value, but in my case I need to be able to inverse the value, so I figured this would work:

#define BIT0 (unsigned char){0b00000001}

void    foo (unsigned char *ptr_bar)
{
    printf("%x", *ptr_bar);
}

void    main    (void)
{
    foo(&~BIT0);
}

Just invert the value with a bitwise NOT (~) when passing it. No biggie right? Well I get the error

error: lvalue required as unary '&' operand

If I rewrite it as:

#define BIT0 (unsigned char){0b00000001}

void    foo (unsigned char *ptr_bar)
{
    printf("%x", *ptr_bar);
}

void    main    (void)
{
    foo(&(unsigned char){~BIT0});
}

First off, this is a super ugly solution, and I really don't want to have to use it in my code. It doesn't feel intuitive at all. Second, It works as I wanted it to work initially, but what I believe that this is telling me is that the '~' operator is promoting the unsigned char to an unsigned int. So I have to cast it back to an unsigned char for the function to be able to accept the address. But the error message doesn't really match up with that which is confusing me.

What's going on here? How do I work around this to get the behavior that I want?

Kalcifer
  • 1,211
  • 12
  • 18
  • 1
    Re "*What's going on here?*", `~` doesn't return an lvalue – ikegami Sep 14 '20 at 23:17
  • @ikegami Ahhhhh okay. I wasn't aware of that. That explains the error. Is there any way to get around it without needing to create a second compound literal? – Kalcifer Sep 14 '20 at 23:24
  • The `~` needs to be applied to the value inside the braces, e.g. `#define NOTBIT0 (unsigned char){~0b00000001}` – Tom Karzes Sep 14 '20 at 23:48
  • 1
    An lvalue is (glossing over some details) an expression that refers to an object. The unary `&` operator requires an lvalue as its argument -- meaning that there has to be some object whose address you're computing. The `~` command applies to and yields a value, not an object. – Keith Thompson Sep 14 '20 at 23:49
  • The names comes from the fact that these are the things you find on the *L*eft-hand side of an assignment operator. What it means is that you can't get the address of something that very likely doesn't have one. – ikegami Sep 15 '20 at 00:05

1 Answers1

0

The reason for the error is that some operators (like unary *) return a so-called modifiable lvalue, meaning a reference to an object that you can change. Most operators don't do this however and ~ is such an operator. All you get from it is a temporary read-only value. It does indeed also promote the operand to signed int, which is often problematic but not the reason for the error.

As for how to solve the problem, it's in how you use these macros. If BIT0 is a compound literal with bit 0 set, then for the sake of consistently one would probably create a similar macro:

#define BITS1_7 (unsigned char){0xFE}

(Note that binary literals aren't standard C.)

But I wouldn't do that either. The root of the problem is overengineering and insisting on using compound literals in strange ways. Instead apply the KISS principle. If you have a function accepting either a single byte or an array, then the most readable way to write the program would be:

#define BIT0 (1u << 0) // industry de facto standard way to write a bit mask
...

uint8_t tmp = BIT0;
foo(&tmp);

alternatively

uint8_t tmp = ~BIT0;

This code can be read and understood by all C programmers.

Lundin
  • 195,001
  • 40
  • 254
  • 396