【问题标题】:An extremely peculiar compiler issue一个极其特殊的编译器问题
【发布时间】:2011-12-14 11:25:08
【问题描述】:

我已经用 C 语言编写了 PIC16F1947 的代码。我正在使用以下代码:

  • MPLAB IDE 8.73
  • HI-TECH C 编译器 9.81

部分代码处理来自 PC 的数据。我从 PC 发送的特定数据包是 325 字节。该数据包如下所示:

data:  0, 64, 1, 0, 255, 255, 255, ... (all 255) ..., 255,   1
index: 0   1  2  3    4    5    6                     323  324

数据包的内容显示为 8 位十进制值(8 位无符号整数)。 micro将其存储在unsigned char的数组中:

unsigned char _command_mgr_buff[330];

unsigned char 是 PIC16F 的 8 位无符号整数。

包的最后一个字节,即索引324,是包的校验和。它是索引 1 到 323 的总和,包括 1 和 323。计算此校验和的 PC 代码(C# 中)如下:

allCertPages[324] = 0;
for (int i = 1; i <= 323; i++)
{
    allCertPages[324] += allCertPages[i];
}

allCertPagesbyte[]

微机必须验证校验和确实是从 PC 传递的值。这是我为PIC16F编写的验证码,包括一些调试信息:

param0 = _command_mgr_buff[324]; // param0 is unsigned int, 16 bit
param1 = _command_mgr_buff[324]; // param1 is unsigned int, 16 bit

// Checksum verification
for (var = 1; var <= 323; var++) // var is unsigned int, 16 bit
{
    _command_mgr_buff[324] -= _command_mgr_buff[var];
    param1 -= _command_mgr_buff[var];
}

if (!_command_mgr_buff[324])
{
    send_status(CS_BAD_PARAM);
}

这个想法是从校验和中减去 [1, 323] 范围内的所有值。如果最终值为 0,则校验和是正确的。否则,如果_command_mgr_buff[324]在减法后发现非零,则校验和不正确。

在调试模式(以及发布模式)下执行代码后,我在_command_mgr_buff[324] 中得到非零值(所以send_status(CS_BAD_PARAM); 被执行,PC 认为有问题),但在param1的低字节!

这怎么可能?!

如果您有兴趣,这里是为非零检查生成的程序集:

  8780                           ;mgr_command.c: 1230: if (!_command_mgr_buff[324])
  8781  0E89  30EA                  movlw   low(8870+0144h)
  8782  0E8A  00D3                  movwf   (??_command_mgr_run+0)^080h+0
  8783  0E8B  3023                  movlw   high(8870+0144h)
  8784  0E8C  00D4                  movwf   (??_command_mgr_run+0)^080h+0+1
  8785  0E8D  0853                  movf    0+(??_command_mgr_run+0)^080h+0,w
  8786  0E8E  0086                  movwf   fsr1l
  8787  0E8F  0854                  movf    1+(??_command_mgr_run+0)^080h+0,w
  8788  0E90  0087                  movwf   fsr1h
  8789                           
  8790  0E91  0881                  movf    indf1,f
  8791  0E92  1D03                  skipz
  8792  0E93  2E95                  goto    u10101
  8793  0E94  2E96                  goto    u10100
  8794  0E95                     u10101:
  8795  0E95  2E9B                  goto    l55070
  8796  0E96                     u10100:
  8797                              line    1232
  8798                              
  8799  0E96                     l55068:    
  8800                           ;mgr_command.c: 1231: {
  8801                           ;mgr_command.c: 1232: send_status(0x11);
  8802  0E96  3011                  movlw   (011h)
  8803  0E97  31B6  2693  3188      fcall   _send_status
  8804                              line    1233
  8805                           ;mgr_command.c: 1233: }
  8806  0E9A  2FE3                  goto    l45048
  8807                              line    1234

这是调试过程中截取的屏幕截图。请检查右侧的 Watch 窗口和第 324 个元素的工具提示。

  • param0 应该是 1
  • param1 &amp; 0xFF 应该是 0
  • _command_mgr_buff[324] 应为 0(工具提示显示 0x0F??!!)

【问题讨论】:

  • 您的固件还在做什么?如果要检查是否是导致问题的校验和计算(不太可能),则应将代码减少为仅使用静态 const 数组中的数据值进行校验和计算。否则,如果它是一个完整的系统,它可能是任何东西:PC 发送另一个字符串,部分覆盖第一个字符串,其他缓冲区溢出,未保留所有必需寄存器的 ASM ISR,等等。
  • 324 是 _command_mgr_buff[324] 的错误索引。顺便说一句 sizeof 可以帮助您避免所有这些文字数字。恕我直言,一个程序应该只包含三个数字:0、1 和 sizeof。
  • @wildplasser:PC和micro之间的通信协议是严格定义的,不会改变。如果需要对协议进行任何更改,则将创建一个新命令。此外,数组 _command_mgr_buff 的大小为 330。sizeof 在这里不起作用。
  • 我的立场是正确的。顺便说一句,你为什么要和allCertPages[324] 相加;你应该很容易地求和一个无符号的临时变量并将最终值分配给allCertPages[324]。 (无符号类型之间的“向下转换”保证是模截断)。WRT sizeof:我的代码片段中仍然有太多的幻数。 (也许你这样做是为了让解释更容易?)
  • @wildplasser:关于汇总到 allCertPages[324],我看不出你的方法和我的做法有什么区别。关于幻数,也许我可以将 WRITE_CERT_CHECKSUM 定义为 324 并使用它。

标签: c compiler-construction embedded microcontroller


【解决方案1】:

如果你的代码如你所说,这里的主要问题是你的测试本身:

if (!_command_mgr_buff[324])
{
    send_status(CS_BAD_PARAM);
}

如果结果是您想要的(即零),您正在发送信号...

这是我不喜欢对非布尔值进行布尔测试的原因之一。布尔变量通常具有提示您读取它们正确捕获的条件的名称,例如if (isLoaded)if (!anyErrors)...这不适用于数字。所以更容易确保你有你想要写出来的语义:

if (_command_mgr_buff[324] != 0) 
{
    send_status(CS_BAD_PARAM);
}

至于为什么你的调试器给你 0x0F...不能帮助你...我的意思是你在零分支。你可以尝试一个干净的构建,看看它是否仍然这样说。 (程序的调试符号可能已经过时了?)

【讨论】:

  • 致 OP:我可以建议采用 MISRA-C 吗?规则 13.2,“应明确对零值的测试,除非操作数实际上是布尔值”。这个错误不会通过 MISRA-C 检查器。数组越界错误的寻址也不会通过一个。声明为Type arr[N]; 的数组不能用arr[N] 访问... C 使用零索引数组。
  • @HostileFork:“零分支”是什么意思?
  • @Donotalo 我没有什么特别的意思……只是值为零的分支(else 分支将用于测试的非零值)。 MISRA-C 对我来说也是新的,有趣...我将不得不研究 MISRA-C++
猜你喜欢
  • 2011-03-17
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多