18

When reading through some example codes for DMAs from Xilinx, I came across this piece of code:

value = (value + 1) & 0xFF

where value is an uint8_t.

What is the point of the & 0xFF? Why not simply write value = value + 1?

wovano
  • 4,543
  • 5
  • 22
  • 49
TheFisherman
  • 1,239
  • 3
  • 13
  • 17
  • 15
    Looks like an experienced coder who has been burned by compilers before. That code works even after someone has come along later and changed the uint8_t or changed the definition of uint8_t... – old_timer Feb 04 '15 at 13:46
  • 7
    Alternatively an artifact of code written before `uint8_t` was used. – Persixty Feb 04 '15 at 14:10
  • That coder was looking out for us. He/she made it obvious that `value` is intended to be an 8-bit value and we don't even have to go looking for the declaration. – kkrambo Feb 04 '15 at 14:23

2 Answers2

18

My guess is that this code was intended to work correctly even if value is not a 1-byte (8-bit) type. The bitmask 0xFF makes sure that only the last byte of the value is kept.

  • 1
    By 1-byte I guess you really mean 8-bit. That programmer was very likely bit in the past by a non-8bit char, then later he changed char to uint8_t but kept the mask. – user3528438 Feb 04 '15 at 14:42
  • @user3528438: yes, I also think something like this was what happened. But we may never know... –  Feb 04 '15 at 14:46
  • 2
    The conversion back to `uint8_t` during assignment already ensures that only the last byte of of the value is kept. So I don't believe this answer is correct. More likely, the masking is there to silence compiler warnings caused by integer promotion. See my answer. – Lundin Feb 04 '15 at 15:29
  • @Lundin : The mask would not suppress a warning, the type of the expression on the right-hand-side of the assignment is `int`. You need an explicit cast to suppress a warning (if you even got one; static analysis tools certainly could generate a warning, but most compilers would not). The mask ensures the intended value regardless of the integral type of value, which may change under maintenance.. – Clifford Feb 04 '15 at 19:05
  • @Clifford I know of at least one compiler (Freescale Codewarrior) that gives such warning, and you can solve it on that compiler with the 0xFF mask. Because then the compiler can tell that the value will never be larger than 255 and therefore optimize away the whole integer promotion. A compiler doesn't need to perform integer promotion as long as it can deduce that omitting the promotion doesn't affect the behavior of the program. – Lundin Feb 05 '15 at 16:05
11

This kind of code is common when you want to avoid problems with implicit type promotions, or when you simply wish to demonstrate that you have considered implicit promotions when you wrote the code, which is good programming practice.

uint8_t is a small integer type and therefore always promoted to int whenever you use it in an expression. The result of (value + 1) is always int.

Without the masking, some compilers give warnings such as "attempting to store int in uint8_t". I've encountered such warnings on several compilers. Theoretically int & 0xFF is still an int, but since it cannot have a value larger than 0xFF, the compiler is likely able to optimize the type down to uint8_t and the warning will go away.

Alternatively you could write value = (uint8_t)(value + 1u); which has the same meaning (but is a MISRA-C compatible version of the code).

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • For the sake of completeness, this is called "applying a mask". By performing a "logical and" operation with 0xFF (255) you are shutting off any bits that would make the value greater than the mask. So this act as incrementing value and reseting it to 255 if it overflows this value. – John-Philip Feb 04 '15 at 18:41
  • 2
    @user2010913 you mean 0, not 255. – Quentin Feb 04 '15 at 19:06
  • @Quentin, 0xFF is 255, if the value overflows 255 it will always be 255 (not 0) – John-Philip Feb 04 '15 at 19:19
  • 2
    @user2010913 If the value gets to 0x100 (256) the 1 will be cut off, sot it wraps back to 0. Or am I nuts ? – Quentin Feb 04 '15 at 19:30
  • @user2010913 That's completely incorrect... It is _bitwise/binary_ AND. The decimal value doesn't mean a thing, it merely masks out the eight least significant bits of the value. If any of the eight lsbs of value are binary ones, they will be preserved. If there are any binary ones outside the 8 lsbs, they will be discarded. This is (should be) really fundamental stuff... no explanation should be need, you should be able to safely assume that a C programmer understands binary arithmetic. – Lundin Feb 04 '15 at 19:40
  • A better way to silence that warning is `++value;` – Ben Voigt Feb 04 '15 at 20:05
  • @BenVoigt Won't make any difference, `value` will still get integer promoted. – Lundin Feb 05 '15 at 07:12
  • @Lundin No it won't. The usual arithmetic conversions are not performed for increment. – Ben Voigt Feb 05 '15 at 07:37
  • @BenVoigt I referred to the integer promotion rules, not "the usual". But I just double-checked, apparently integer promotion is indeed not used during ++, while it is used in all other cases of unary operators (+ - ~ ! etc). Yet another inconsistency in the C language. Still, I wouldn't count on the average C programmer to be aware of such subtle details. – Lundin Feb 05 '15 at 07:50
  • It's not an inconsistency. All those other operators read their operands only. Increment and decrement modify the operand. Promotions and conversions (which result in rvalues) are never applied to an operand that is modified (must be an lvalue). Perfectly consistent across all the forms of assignment and compound assignment (increment effectively is a compound assignment, `value += 1;`) – Ben Voigt Feb 05 '15 at 08:18