首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C中调用程序集函数,反转字符串

在C中调用程序集函数,反转字符串
EN

Stack Overflow用户
提问于 2013-05-06 19:53:19
回答 3查看 1.5K关注 0票数 2

我正在尝试编写一个c程序,它将调用汇编函数来反转字符串。然而,我很难让汇编代码遍历字符串以找到结束字符"0“。

我的C代码如下:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

// These functions will be implemented in assembly:
//
void strrev(char *str) ;

int main(){
    char *str1;
    str1 = strdup("Hello World");
    strrev(str1);
    printf("str1 reversed: \"%s\"\n", str1) ;
    free(str1);
    return 0;
}

我的任何汇编代码都很简单:

代码语言:javascript
复制
; File: strrev.asm
; A subroutine called from C programs.
; Parameters: string A
; Result: String is reversed and returned.


    SECTION .text
    global strrev
_strrev: nop
strrev:
    push    ebp
    mov ebp, esp

    ; registers ebx,esi, and edi must be saved if used
    push ebx
    push edi

    xor esi, esi    
    xor eax, eax
    lea ecx, [esp+8]    ; load the start of the array into ecx
    jecxz   end     ; jump if [ecx] is zero
    mov edi, ecx

reverseLoop:
    cmp byte[edi], 0
    je  end
    inc     edi 
    inc eax
    jmp reverseLoop

end:
    pop edi     ; restore registers
    pop ebx
    mov esp, ebp    ; take down stack frame
    pop ebp
    ret

我现在要做的就是简单地遍历字符串,直到它在reverseLoop中找到结尾。但是,如果我尝试使用gdb并逐步执行程序,在查看第一个字符"H“后似乎立即失败。

在使用GDB运行时,在第25行中断,同时使用"display/c $edi“显示edi寄存器,将产生以下输出:

代码语言:javascript
复制
(gdb) 
reverseLoop () at strrev.asm:25
25      cmp byte[edi], 0
1: /c $edi = 72 'H'

这是正确的,但如果我深入到inc edi,edi立即变得不正确。它应该是"e“,因为"Hello World”中的第二个字符是"E“。但是,gdb输出将其列为"I":

代码语言:javascript
复制
27      inc     edi 
1: /c $edi = 72 'H'
(gdb) 
28      inc eax
1: /c $edi = 73 'I'

当我迭代edi寄存器时,我做错了什么吗?

EN

回答 3

Stack Overflow用户

发布于 2013-05-06 20:00:31

您将edi寄存器的内容打印为字符,但其内容实际上是一个地址。您真正想要的是显示edi中的地址指向的内容。

也许可以试一下

代码语言:javascript
复制
display *((char *) $edi)
票数 0
EN

Stack Overflow用户

发布于 2013-05-06 20:00:41

mov cl, [ebp+8]代替lea ecx, [esp+8]怎么样?

票数 0
EN

Stack Overflow用户

发布于 2013-05-06 20:29:51

在函数prologue设置EBP之后,第一个参数是[ebp + 8] [esp + 8]。自从进入strrev()以来,您已经推送了EBP、EBX、EDI,因此在您想要访问函数的第一个参数时移动了ESP。你也应该使用MOV而不是LEA,否则你会得到((char*)$ebp + 8)而不是(*(char**)($ebp + 8))

最初的mainLoop试图通过一次处理4个字节来实现strlen(),但是只有在一次处理一个字节的情况下,检测'\0'字节和维护字符串长度计数的方法才是正确的。

另一个不相关的错误是,您在EAX中存储(字符串长度/ 2),但在交换字符时也使用AL作为临时存储。AL是EAX的最低字节,字符串长度被破坏,字符交换循环在正确的迭代次数后不会终止。

请看下面的补丁,其中我修复了您问题的早期版本的代码:

代码语言:javascript
复制
--- strrev.asm.orig 2013-05-06 20:25:58.000497875 +0800
+++ strrev.asm  2013-05-06 20:26:29.583835308 +0800
@@ -17,16 +17,37 @@

     xor esi, esi    
     xor eax, eax
-    lea ecx, [esp+8]    ; load the start of the array into ecx
+    mov ecx, [ebp+8]    ; load the start of the array into ecx
     jecxz   end     ; jump if [ecx] is zero
-    mov edi, ecx
+
+mainLoop:
+    add eax, 1      ; icn eax would work as well
+    add     ecx, 1      
+    mov dl, [ecx]  ; load ecx
+    cmp dl, 0      ; compare with 0
+    je  reverseLoop     ; if ecx is zero, we're done
+    jmp mainLoop        ; if ecx isn't zero, keep looping
+

 reverseLoop:
-    cmp byte[edi], 0
-    je  end
-    inc     edi 
-    inc eax
-    jmp reverseLoop
+    mov ecx, [ebp + 8]  ; reload the start of the array into ecx
+    mov esi, ecx    ; esi points to start of array
+    add ecx, eax
+    mov edi, ecx    
+    dec edi ;edi points to end of array
+    shr eax, 1  ;eax is the count
+    jz  end ; if string is 0 or 1 chars long, jump to end
+
+reverseLoop_1:
+
+    mov cl, [esi]   ;load initial array
+    mov bl, [edi]   ;load end of array
+    mov [esi], bl   ;swap
+    mov [edi], cl
+    inc esi
+    dec edi
+    dec eax     ;loop
+    jnz reverseLoop_1   

 end:
     pop edi     ; restore registers
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16398208

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档