Forth数组填充异常求助:循环执行时覆盖前序元素
别抱歉!Forth的栈操作和内存模型确实容易踩坑,咱们一步步来拆解你的问题,把数组操作的逻辑理清楚。
问题核心:栈顺序错误 + 未考虑CELL字节偏移
你的代码里有两个关键错误,直接导致循环填充时完全没有正确操作数组元素:
1. [!]的栈顺序完全搞反了
Forth的栈是后进先出(LIFO),栈注释格式( 输入1 输入2 ... -- 输出1 输出2 ... )中,最左边的输入是最先压入栈的,最右边的是最后压入的(栈顶)。
你定义的: [!] ( value index array -- ) + ! ;,如果想实现“把value写到array的index位置”,正确的栈输入顺序应该是( array index value -- )——因为你需要先压入数组起始地址mass,再压入索引I,最后压入值I,此时栈顶是value,中间是index,最下面是array。
但你的[!]里直接执行+ !,实际做的是:
- 取栈顶的
value和index相加,得到value+index - 然后把栈里剩下的
array当作地址,把相加结果写到这个地址里?
这完全不是你想要的逻辑!你真正需要的是:计算array + index * CELL(每个元素占1个CELL的字节数),再把value写到这个地址。
2. 未考虑CELL的字节大小
Forth中,CELL代表当前系统中一个单元格的字节数(32位系统是4,64位是8)。数组的每个元素对应一个CELL,所以索引I对应的字节偏移是I * CELL,而不是直接用I。你的show和[!]里都直接用I +,这是按字节偏移,不是按CELL偏移,导致操作的是同一个CELL的不同字节,或者完全错误的地址。
循环里到底发生了什么?
我们以第一次循环(I=0)为例,一步步看栈操作:
- 执行
I I mass [!]:- 压入
mass→ 栈:[mass] - 压入
I(0) → 栈:[mass, 0] - 压入
I(0) → 栈:[mass, 0, 0]
- 压入
- 进入
[!]:- 执行
+:取栈顶两个元素0和0相加,得到0,栈变成[mass, 0] - 执行
!:!的栈逻辑是( value addr -- ),也就是把栈顶第二个元素(mass)当作值,栈顶第一个元素(0)当作地址,把mass写到地址0里——这完全是非法内存操作,会破坏系统数据!
- 执行
后面的循环同理,每次都在错误的地址写入错误的值,所以你看到的是乱码输出。
为什么直接赋值3 3 mass !看起来正常?
你说执行3 3 mass !后show输出3 0 0 0,这其实是巧合:
- 假设你的
?是读取单个字节的值,mass是数组起始地址(比如1000) - 执行
3 3 mass !时,栈顺序是[3, 3, mass],!会取栈顶的mass(地址1000)和3(值),把3写到1000地址 show循环读取mass+0(字节3)、mass+1(字节0)、mass+2(字节0)、mass+3(字节0),所以输出3 0 0 0——但实际上你只是写了第一个CELL的第一个字节,不是整个CELL元素。
正确的数组操作实现
我们重新写正确的代码,解决上述问题:
1. 定义数组操作词
\ 创建2x2的数组,共4个CELL空间 create mass 2 2 * CELLS ALLOT \ 数组赋值词:( value index array -- ) \ 功能:把value写到array的第index个CELL位置 : cell! ( value index addr -- ) CELLS + \ 计算目标地址:addr + index*CELL ! \ 把value写入该地址 ; \ 数组读取词:( index array -- value ) \ 功能:读取array的第index个CELL值 : cell@ ( index addr -- value ) CELLS + \ 计算目标地址 @ \ 读取地址中的值 ; \ 遍历输出数组所有CELL元素 : show 4 0 DO I mass cell@ \ 读取第I个元素 . \ 输出元素(带空格) LOOP CR \ 换行 ;
2. 正确的循环填充词
: fill 4 0 DO I I mass cell! \ 把值I写入第I个元素位置 show \ 显示当前数组状态 LOOP ;
3. 测试执行
现在执行fill,输出会是:
0 0 0 0 0 1 0 0 0 1 2 0 0 1 2 3
执行3 0 mass cell!(把3写入第0个元素),再执行show,输出:
3 1 2 3
关键知识点总结
- Forth的栈顺序绝对不能搞反,写每个词时一定要先理清栈的输入输出逻辑。
- 操作数组时,必须用
CELLS计算偏移,因为每个元素是CELL大小,直接用+是字节偏移,会导致地址错误。 !和@的栈规则:!是( value addr -- )(值在前,地址在后,把值写入地址);@是( addr -- value )(地址在前,读取后返回值)。
内容的提问来源于stack exchange,提问作者Kmssr

