The first split vote about C compiler random test
blog.regehr.org
blog.regehr.org
Ether way, as it's a 1-bit field, the value can only be 0 or 1 and is always > -3.
It's just astounding how many developer-years have been wasted by the industry's insistence on producing and accepting wrong answers when commodity hardware has been maintaining under/overflow flags for literally my entire career.
Conversion of out-of-range values to signed types is, however, implementation-defined (and also allows an implementation-defined signal to be raised, which in practice makes it something to avoid in correct programs).
http://stackoverflow.com/questions/50605/signed-to-unsigned-...
A computation involving unsigned operands can
never overflow, because a result that cannot be
represented by the resulting unsigned integer type
is reduced modulo the number that is one greater
than the largest value that can be represented by
the resulting unsigned integer type.For the second part of your comment, comparisons are always done between the same type. In this case, either the -3 is promoted to unsigned (which gives the result UINT_MAX - 2, a large positive number) or the bitfield is promoted to int. It is the rules for this type promotion that is the heart of the problem.
But in general, uninitialized variables aren't even random in C. Accessing them is undefined. That means you aren't guaranteed to get a bogus value, you could just as well get a nasal demon.
while (update_thing(&foo), foo != 0) {
}It's useful shorthand in a few specific cases, but I would argue that this sort of code is bad style and if I was in charge (which I am not) it would be in the "never do this" section.
That this edge case apparently took decades to find implies (to me) that the comma operator is not to be used in production code. I'll always choose a few extra lines of code over ambiguity, because someday other will people have to read my code when there's some kind of a problem with it, and I'd rather make the problems obvious, not subtle. :)
update_thing(&foo);
while (foo != 0) {
/* loop body */
update_thing(&foo);
}
Unnecessary duplication like this is itself a potential source of bugs - it's all to easy to update one but not the other. The other alternative is to hack up the loop to exit in a strange place: while (1) {
update_thing(&foo);
if (foo == 0)
break;
/* loop body */
}
This is arguably even worse - the actual loop termination condition is not where you expect to find it anymore. Personally, I find the formulation using the comma operator to be completely clear.Note that the bug referenced here is more about the subtleties of bitfield type promotion in expressions, and the interplay of bitfields with operators that evaluate to the type of one of their arguments, than it is about the comma operator. You can show the same bug using the assignment operator instead of the comma operator.
If you're going consign anything involved here to the "never do this" section, I'd start with any use of bitfields, and maybe also include mixing unsigned and signed types in expressions without explicit conversion.
That is, if you don't mind tying your code to a particular platform and compiler. The alignment and packing of bitfields is implementation dependent: http://stackoverflow.com/questions/1490092/c-c-force-bit-fie...
For what it's worth, I use bitfields on occasion, but only to save memory and get better warnings when storing range-limited values. It's nice to have the compiler warn about a comparison always being true or false due to the limited range of a type. The performance of bitfields is probably worse than just tossing all my boolean values into an int32_t or int8_t.
The only portable uses of bitfields are to potentially save a little memory, and to get "modulo-power-of-2" behaviour. Bitfields are a classic "not as useful as they first appear" feature.
do {
update_thing(&foo);
} while (foo != 0); bool second_loop = false;
do
{
if (second_loop) {...}
update_thing(&foo);
second_loop = true;
} while (foo != 0);
The real question is which is more clear when some else looks at the code. I suspect even if it's shorter and faster while (foo,bar) is less clear to most people than the above code.The comma operator, while not all that common, can be pretty useful in common situations, and can easily achieve both clarity and conciseness. For example, to iterate over a singly-linked list while keeping a pointer to the previous item:
for (prev = NULL, this = first; this != NULL; prev = this, this = this->next) {
...
}
I think this is a perfectly reasonable way to do things. Disallowing the comma operator due to an underspecified interaction with bitfields would be throwing out the baby with the bathwater, in my opinion.(oops, read further down that this is a common pattern. I need to go read K&R again!)