You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HLA汇编程序内存访问违例修复及字符串长度计算问题求助

HLA汇编程序内存访问违例修复及字符串长度计算问题求助

我现在卡在一段HLA汇编代码上了,想做的功能很简单——计算用户输入字符串的长度,预期运行效果是这样的:

Feed Me: asdf
The String You Entered: asdf Has Length = 4

但折腾了好半天程序要么跑不对,还时不时报内存访问违例。HLA的资料实在太少了,我怀疑是栈操作哪里出问题了,有没有懂的大佬帮我看看啊?

我的代码是这样的:

program strlenDemo;
#include("stdlib.hhf");

static
    length: byte;
    inputString: string;

procedure strlen( baseStringAddress: dword ); @nodisplay; @noframe;
begin strlen;
    mov( baseStringAddress, esi );  // Load the base address of the string into ESI
    mov( 0, ecx );                  // Clear ECX, it will be used as the character counter
    
    strlen_loop:
        mov( [esi], al );           // Load the current byte of the string into AL
        test( al, al );             // Test if AL is zero (null character)
        jz strlen_done;             // If zero, jump to strlen_done
        // 这里漏了递增指针和计数器的步骤!
        jmp strlen_loop;
    
    strlen_done:
        // 也没处理返回值和寄存器恢复
end strlen;

// 主程序里也没正确调用这个过程和输出结果
begin strlenDemo;
    // 缺少获取输入和调用逻辑
end strlenDemo;

问题分析

  • 字符串遍历逻辑缺失:循环里只读取了第一个字符,没有递增ESI指针移动到下一个字符,也没给计数器ECX加1,要么死循环要么读越界内存导致违例。
  • 栈帧与寄存器管理问题:用了@noframe就意味着要手动处理寄存器保存恢复、参数传递和返回操作,原代码完全没做这些,会破坏主程序的寄存器状态,甚至导致返回地址错误。
  • 返回值未传递:原strlen过程没把计算好的长度返回给主程序,主程序也没调用过程的逻辑。
  • 输入输出与内存管理缺失:主程序里没写获取用户输入、输出结果的代码,也没处理stdin.gets分配的内存。

修复后的完整代码

program strlenDemo;
#include("stdlib.hhf");

static
    inputString: string;

procedure strlen( baseStringAddress: dword ); @nodisplay; @noframe;
begin strlen;
    // 保存非易失性寄存器ESI(HLA调用约定里ESI需要保留)
    push( esi );
    // 虽然ECX是易失性的,但这里保存一下更稳妥
    push( ecx );

    mov( baseStringAddress, esi );  // 把字符串基地址载入ESI
    mov( 0, ecx );                  // 计数器ECX初始化为0
    
    strlen_loop:
        mov( [esi], al );           // 读取当前字节到AL
        test( al, al );             // 检查是否是字符串结束符(0)
        jz strlen_done;             // 是结束符就跳转到结束逻辑
        
        inc( ecx );                 // 计数器加1
        inc( esi );                 // 指针移动到下一个字符
        jmp strlen_loop;            // 继续循环
    
    strlen_done:
        mov( ecx, eax );            // 把计算好的长度放到EAX里返回(HLA默认用EAX返回整数)
        // 恢复之前保存的寄存器
        pop( ecx );
        pop( esi );
        ret();                      // 手动返回(@noframe需要自己调用ret)
end strlen;

begin strlenDemo;
    stdout.put("Feed Me: ");
    stdin.gets( inputString );      // 获取用户输入的字符串
    
    push( inputString );            // 把字符串地址压栈作为参数
    call strlen;                    // 调用strlen,结果存在EAX里
    add( 4, esp );                  // 清理栈上的参数(因为传了一个dword,占4字节)
    
    // 输出结果
    stdout.put( "The String You Entered: ", inputString, " Has Length = ", eax, nl );
    
    free( inputString );            // 释放stdin.gets分配的内存,避免泄漏
end strlenDemo;

关键修复点说明

  • 寄存器保护:因为用了@noframe,必须手动保存ESI这类非易失性寄存器,否则会干扰主程序的寄存器状态。
  • 遍历逻辑补全:每次循环后递增ESI和ECX,确保逐个字符遍历直到结束符。
  • 返回值处理:用EAX传递返回值,这是HLA里整数返回的常规方式。
  • 栈操作规范:调用过程前压入参数,调用后手动清理栈(符合cdecl风格的调用约定)。
  • 内存管理:记得释放stdin.gets分配的动态内存,避免内存泄漏。

这样修改后程序应该就能正常运行,准确计算字符串长度啦~

备注:内容来源于stack exchange,提问作者Tiburcio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 09:03:06