C无符号模数会导致编译器警告

时间:2013-11-14 14:46:04

标签: c embedded compiler-warnings

在MC9S12C32微控制器的一些嵌入式C代码中,我有一个循环队列(也称为循环缓冲区),实现了一个静态大小的字节数组和两个用于队列前后的“指针”,实际上只是索引对于队列的数组。

// 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

请注意,trear是后方元素的实际索引,但tfront比前元素的实际索引小1(当然,以16为模)。因此,例如,如果我的缓冲区包含“hello”,它可能看起来像这样(空槽是垃圾值):

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

当从队列中删除一个字节时,我这样做:

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

这一切都运行正常 - 至少,我的程序没有展示与此片段相关的错误。但是,当我编译这个程序时,我的编译器警告我上面片段中标记为(A)的行,抱怨:

  

警告:C2705:可能丢失数据

     

main.c第402行

线402是线(A)。我应该注意到我没有使用gcc之类的东西;我正在飞思卡尔的CodeWarrior IDE中进行编译,它有时会给我一些有些神秘的警告。为了摆脱警告,我将上面的片段重写为:

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

但是,我的编译器仍会发出相同的警告,这次是关于行(B)。也许编译器告诉我在语句(tfront + 1 >= tsize)中,tfront在执行之前可能是255,并且溢出。当然,我知道这不会发生,但我的编译器却没有。

如果是这种情况,为什么第(A)行成为问题?基本上,我想知道编译器不满意的是什么。


自输入我的问题以来,我已经通过将tsize从变量类型更改为预处理器定义(即#define TSIZE 16)来解决它。不过,我的问题仍然存在。


一些相关问题:
unsigned overflow with modulus operator in C
modulus operator with unsigned chars

2 个答案:

答案 0 :(得分:5)

编译器警告可能来自于tfront %= tsize;中相当于tfront = tfront % tsize;的事实,因为C中的提升规则表达式tfront % tsize具有(*)类型{{1 }}

如果您改为编写int,它可能会使编译器静音。

没有特别的理由担心,你的编译器确实会发出奇怪的警告:虽然表达式tfront = (byte)(tfront % tsize);在技术上具有类型tfront % tsize,但它的值都适合int因为它的计算方式。即使这些值都不适合byte,无条件整数类型的C标准也保证了环绕行为(这样你就可以正当地使用这种环绕行为)。 / p>

(*)除非您的编译平台byte不能包含int可以采用的所有值,在这种情况下它将是unsigned char类型,您可能看不到警告。

答案 1 :(得分:3)

此特定警告是所有Codewarrior编译器中的已知错误。可能丢失的数据警告是不一致的和错误的。有时它会发出警告,因为存在隐式类型促销的风险,有时却没有。见this discussion。我可以确认对于S12C的CW也是如此(至少到5.1版本,我正在使用它)。

你可以禁用此警告,但我不推荐它,因为它有时会发现危险的代码,以及错误的警告。在您的具体情况中:警告是正确的。

您正在对小整数类型进行算术运算,而不进行显式转换。这样的代码是危险的,可能包含隐藏的错误,因为C将小整数提升为签名的整数。像MISRA-C这样强制执行显式转换的编码标准在这里会有所帮助。

此外,除非您知道它是如何工作的,否则?:运算符也可能是危险的。它平衡了第二和第三个操作数,这可能是人们没想到的。

因此我建议将代码更改为:

tfront = (byte)(tfront % tsize); 
...

if( (byte)(tfront +1) >= tsize)
{
  tfront = 0;
}
else
{
  tfront++;
}