health 发表于 2026-1-30 13:51:40

C251又出bug了,有关数组下标减法计算 | 这段程序对于C251来说是错误的程序 ?

举例说明,向位于xdata的数组写入数据

void write(uint16_t *dst, uint16_t dat)
{
    int i;      
      
    for (i = 15; i >= 0; i--)
    {
      dst = dat;
    }
}

假设当前i=14,显然 dst 应该是访问 dst。
dst如果是xdata空间起始的数组,即地址为0x10000,那么dst的地址应该是0x10002。
然而,C251编译出的代码并非如此。看汇编代码

XRL      WR6,WR6
SUB      WR6,WR28
SLL      WR6
ADD      WR6,WR10
MOV      WR4,WR8
MOV      @DR4+0x001E,WR30

设i=14
先计算 0 - i 的值即 -i ,得到-14(即 0xFFF2);
因为是int16类型,所以左移一位相当于乘以2,得到 -28(即 0xFFE4);
"-i"加上dst地址低16位,即0xFFE4 + 0x0000 = 0xFFE4;存入DR4结果为0x01FFE4。
最后用(@DR4+0x001E)作为内存的地址访问。
写入地址即0x01FFE4 + 0x001E;直接按算术加法来的话结果是0x020002,已经不是xdata空间了。
而我们需要写入的地址是0x010002。

在实际芯片上运行,该写入的地址没有写入,反而乱修改其它变量。
此bug极为隐晦,看C代码看不出一点问题。
程序调试好几天,各种奇怪的故障,动不动崩溃,找不出原因。
明明这个模块改动一个小地方,却对其它模块的变量造成严重破坏,皆是因此而来。

结论:C251下数组下标慎用减法计算。




lcwswust 发表于 2026-1-30 14:32:53

没看太明白,意思是先加15后减14和先减14后加15的结果不一样?真奇怪。

health 发表于 2026-1-30 15:08:09

主要是内存分区的原因,高位地址,也表示内存类型,有时候计算考虑高位地址,有时候又忽略高位地址,造成混乱。

ercircle 发表于 2026-1-30 15:31:29

用无符号i可以,想起上一个有符号无符号问题:
C语言有符号数进位陷阱,进来看了多活一小时 - C语言,汇编语言,Proteus MCU软件仿真 国芯人工智能技术交流网站 - AI32位8051交流社区





网老四 发表于 2026-1-30 15:46:13

访问存储器的地址都是无符号整数.所以用作处理存储地址的变量,最好转换成无符号整数类型

Ayb_ice 发表于 2026-1-30 16:41:41

没看懂

Ayb_ice 发表于 2026-1-30 16:44:08

这是完整的汇编,应该没有问题的
; line 695: void write(u16 *dst, u16 dat)
      MOV      WR30,WR6
;---- Variable 'dat' assigned to Register 'WR30' ----
      MOV      DR8,DR0
;---- Variable 'dst' assigned to Register 'DR8' ----
; line 696: {
; line 697:   int i;      
; line 698:         
; line 699:   for (i = 15; i >= 0; i--)
      MOV      WR28,#0FH
;---- Variable 'i' assigned to Register 'WR28' ----
?C0182:
; line 700:   {
; line 701:         dst = dat;
      XRL      WR6,WR6
      SUB      WR6,WR28
      SLL      WR6
      ADD      WR6,WR10
      MOV      WR4,WR8
      MOV      @DR4+0x1E,WR30
; line 702:   }
      DEC      WR28,#01H
      CMP      WR28,#00H
      JSGE   ?C0182
; line 703: }
      ERET   

health 发表于 2026-1-30 17:39:44

Ayb_ice 发表于 2026-1-30 16:44
这是完整的汇编,应该没有问题的
; line 695: void write(u16 *dst, u16 dat)
      MOV      WR30,WR6


实测一下就知道了,可以用keil软仿真。

21cnsound 发表于 2026-1-30 17:54:42

严格来说,这个“问题”不算BUG,从整体设计上是没问题的。

health 发表于 2026-1-30 17:58:12


传入参数dst = xdata首地址0x10000
; line 695: void write(u16 *dst, u16 dat)
      MOV      WR30,WR6
;---- Variable 'dat' assigned to Register 'WR30' ----
      MOV      DR8,DR0
;---- Variable 'dst' assigned to Register 'DR8' ----变量dst分配给DR8
; line 696: {
; line 697:   int i;      
; line 698:         
; line 699:   for (i = 15; i >= 0; i--)
      MOV      WR28,#0FH                  ;i = 15
;---- Variable 'i' assigned to Register 'WR28' ----变量i分配给WR28
?C0182:
; line 700:   {
; line 701:         dst = dat;
      XRL      WR6,WR6       ;WR6清零
      SUB      WR6,WR28   ;WR6 = 0 - i = -15 = 0xFFF1
      SLL      WR6               ;WR6 = -15 * 2 = -30 = 0xFFE2
      ADD      WR6,WR10   ;WR6 = 0xFFE2 + dst低16位 = 0xFFE2
      MOV      WR4,WR8       ;WR4 = dst高16位即0x0001,此时DR4 = 0x0001FFE2
      MOV      @DR4+0x1E,WR30   ;数据写入地址 = = ,本应写入dst即地址才对。
; line 702:   }
      DEC      WR28,#01H
      CMP      WR28,#00H
      JSGE   ?C0182
; line 703: }
      ERET

页: [1] 2 3
查看完整版本: C251又出bug了,有关数组下标减法计算 | 这段程序对于C251来说是错误的程序 ?