Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C unsigned modulus causes compiler warning

In some embedded C code for the MC9S12C32 microcontroller, I have a circular queue (aka circular buffer) implemented with a statically sized byte array and two "pointers" for the front and rear of the queue, which are in reality just indices for the queue's array.

// call unsigned chars bytes
typedef unsigned char byte;
byte trear  = 0;   // SCI transmit display buffer IN index
byte tfront = 0;   // SCI transmit display buffer OUT index
byte tsize  = 16;  // size of transmit buffer
byte tbuf[16]= {0};// SCI transmit display buffer

Note that trear is the actual index of the rear element, but tfront is one less than actual index of the front element (subject to modulo 16 of course). So, for example, if my buffer contained "hello", it might look like this (where empty slots are garbage values):

_________________________________
| | |h|e|l|l|o| | | | | | | | | |
   ^         ^
 front      rear

When it's time to remove a byte from the queue, I do this:

// increment front index
tfront++;
// wrap front index if it exceeded bounds
tfront %= tsize;                           // (A)
// get character to transmit
byte outputChar = tbuf[tfront];

This all works fine -- at least, my program has exhibited no bugs related to this fragment. However, when I compile this program, my compiler warns me about the line marked (A) in the fragment above, complaining:

Warning : C2705: Possible loss of data

main.c line 402

Line 402 is line (A). I should note that I am not using gcc or the like; I am compiling in Freescale's CodeWarrior IDE, which has sometimes given me other somewhat mystifying warnings. In an attempt to get rid of the warning, I rewrote the fragment above as:

// increment front index mod tsize
tfront = (tfront + 1 >= tsize) ? 0 : tfront + 1;      // (B)
// get character to transmit
byte outputChar = tbuf[tfront];

However, my compiler still emits the same warning, this time about line (B). Maybe the compiler is telling me that in the statement (tfront + 1 >= tsize), tfront might be 255 before execution, and overflow. Of course, I know this won't happen, but my compiler doesn't.

If this is the case, though, why was line (A) an issue? Basically, I'd like to know what the compiler is unhappy about.


Since typing out my question, I've solved it by changing tsize from a variable type to a preprocessor definition (i.e., #define TSIZE 16). My question still stands, though.


Some related questions:
unsigned overflow with modulus operator in C
modulus operator with unsigned chars

like image 757
ravron Avatar asked Nov 14 '13 14:11

ravron


1 Answers

The compiler warning likely comes from the fact that in tfront %= tsize;, which is equivalent to tfront = tfront % tsize;, because of promotion rules in C the expression tfront % tsize has(*) type int.

It may silence the compiler if you write tfront = (byte)(tfront % tsize); instead.

There is no particular reason to worry, and your compiler emits strange warnings indeed: although the expression tfront % tsize technically has type int, its values all fit in a byte because of the way it is computed. Even if the values did not all fit in a byte, the wrap-around behavior is guaranteed by the C standard for unsigned integer types (so that you would be justified in using this wrap-around behavior on purpose).

(*) unless on your compilation platform int cannot contain all values that an unsigned char can take, in which case it would be of type unsigned int and you probably wouldn't see the warning.

like image 60
Pascal Cuoq Avatar answered Sep 19 '22 13:09

Pascal Cuoq