6

在 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比前元素的实际索引小一(当然以模 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];

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

但是,如果是这种情况,为什么线路(A)是一个问题?基本上,我想知道编译器对什么不满意。


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


一些相关问题:
C 模运算符中的无符号溢出与模运算
符与无符号字符

4

2 回答 2

5

编译器警告可能来自这样一个事实,即 intfront %= tsize;相当于tfront = tfront % tsize;,由于 C 中的提升规则,表达式tfront % tsizehas(*) 类型int

如果您tfront = (byte)(tfront % tsize);改为编写,它可能会使编译器静音。

没有什么特别需要担心的,而且你的编译器确实会发出奇怪的警告:虽然表达式在tfront % tsize技术上具有 type int,但由于它的计算方式,它的值都适合 a byte。即使这些值不完全适合 a byte,C 标准也保证了无符号整数类型的环绕行为(这样你就有理由故意使用这种环绕行为)。

(*) 除非在您的编译平台int上不能包含 anunsigned char可以采用的所有值,在这种情况下它将是 typeunsigned int并且您可能不会看到警告。

于 2013-11-14T14:53:59.773 回答
3

此特定警告是所有 Codewarrior 编译器中的一个已知错误。可能的数据丢失警告是不一致和错误的。有时它会发出警告,因为存在隐式类型提升的风险,有时则不会。请参阅此讨论。我可以确认这对于 S12C 的 CW 也是如此(至少就我正在使用的 5.1 版而言)。

您可以禁用此警告,但我不建议这样做,因为它有时会发现危险代码以及错误警告。与您的具体情况一样:警告是正确的。

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

此外, ?: 运算符也可能很危险,除非您知道它是如何工作的。它平衡了第 2 和第 3 个操作数,这可能是人们没有预料到的。

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

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

if( (byte)(tfront +1) >= tsize)
{
  tfront = 0;
}
else
{
  tfront++;
}
于 2013-11-15T08:02:05.213 回答