带一个char数组和2个索引的函数;交换那些索引中的字符

如何解决带一个char数组和2个索引的函数;交换那些索引中的字符

这是我的函数原型:

char* swap(char* array,int index1,int index2);

这是我的汇编代码

segment .text
   global swap

swap:
   mov r14,[rdi+rsi]
   mov r15,[rdi+rdx]
   
   mov [rdi+rsi],r15        ;this line segfaults
   mov [rdi+rdx],r14
   
   mov rax,rdi
   
   ret

mov [rdi+rsi],r15 mov [rdi+rdx],r14 行给我一个错误;我不确定我要去哪里哪里

调用函数

 #include <stdio.h>
 #include <stdlib.h>

 extern char* swapLetters(char* str,int indexA,int indexB);

 int main()
 {    
    char* st= "E X A M P L E";
    printf("Before swap: \t%s\n",st);

    char * res = swap(st,2,10);

    printf("After swap: \t%s\n",res);
    return 0;
}

预期输出

交换之前:E X A M P L E

交换后:E L A M P X E

解决方法

主要问题是您的st变量被定义为指向字符串文字的指针。

char* st= "E X A M P L E";
C 语言中的

String literals被视为只读。修改这样的字符串是未定义的行为。发生的情况是未知的,并且将特定于编译器及其运行环境。当您要在汇编代码中写入该内存时,您的环境正在引发异常。在大多数使用现代编译器的现代OS上,字符串文字被放置在不可写的内存中,因此它将生成异常,这就是您所遇到的情况。

如果您希望在可写内存中创建字符数组,可以通过以下方式定义st

char st[] = "E X A M P L E";

发布了汇编代码

一个问题是您对函数swap的索引是int。在64位 GCC / CLANG 中,int是32位。如果将32位带符号的int传递给汇编代码,则前32位可能包含垃圾信息。鉴于索引永远不会为负,因此应使用无符号类型,最好使用64位类型。我建议改为使用size_t类型。 size_t在x86-64代码中将是无符号的,并且大小为64位,因此当传递给汇编代码时,您无需在使用索引值之前对其进行零位签名/将其扩展为64位。我建议将swap更改为:

char* swap(char* array,size_t index1,size_t index2)

如果您将index1index2保留为有符号整数(int),则汇编代码的开头必须在两个 ESI上都使用MOVSX EDX 寄存器。该代码如下所示:

swap:
    movsx rsi,esi        ; Sign extend 32-bit index1 parm in ESI to 64-bits
    movsx rdx,edx        ; Sign extend 32-bit index2 parm in EDX to 64-bits
    ; rest of function here

如果要对unsigned intindex使用32位index2,则必须使用以下方法对32位值进行零扩展:

    mov esi,esi          ; Zero extend 32-bit index1 parm in ESI to 64-bits
    mov edx,edx          ; Zero extend 32-bit index2 parm in EDX to 64-bits
    ; rest of function here

当操作的目标是64位模式下的32位寄存器时,CPU自动将目标寄存器的高32位清零。将像 ESI 这样的32位寄存器移到其自身将清除 RSI 的高32位。所有通用寄存器都一样。


根据{{​​3}},

RBX RBP R12–R15 是非易失性寄存器。如果您的函数修改了它们,则必须保留其内容。您可以将它们压入堆栈,并在完成后将其原始值弹出堆栈。首选方法是使用不需要保留的易失性寄存器之一,例如 R8-R11 RAX RCX RDX RDI RSI


使用64位寄存器将数据移入/移出内存时,将传输64位(8字节)。例如:

mov r14,[rdi+rsi]

移动从内存地址[rdi+rsi]开始的8个字节,并将其移动到64位寄存器 R14 。稍后的写操作执行类似的操作,但更新内存中的8个字节而不是一个字节。如果将字符数组放在堆栈上,则更新8个字节的数据可能会破坏堆栈,在您的代码和环境中就是这种情况。

在使用编号为 R8 R15 的寄存器时,可以通过在寄存器名称的末尾添加b后缀来引用低8位({ {1}}用于16位字,w用于32位双字。对于64位模式,使用NASM / YASM语法的所有寄存器名称中的x86-64 System V ABI是:

complete chart

d将被写为mov mov r14,[rdi+rsi]以移动单个字节。您还必须对其他每个动作都进行更改。


假设您将mov r14b,[rdi+rsi]index1更改为类型index2(或size_t),则汇编代码可能被编写为:

uin64_t

如果要使用其他易失性寄存器而不是非易失性寄存器,则代码可以简化为:

segment .text
global swap

swap:
   push r14           ; Save non-volatile registers we overwrite
   push r15

   mov r14b,[rdi+rsi] ; Move one byte from [rdi+rsi] to R14B. R14B is lower 8 bits of R14
   mov r15b,[rdi+rdx] ; Move one byte from [rdi+rdx] to R15B. R15B is lower 8 bits of R15
   mov [rdi+rsi],r15b ; Move the byte in R15B to [rdi+rsi]
   mov [rdi+rdx],r14b ; Move the byte in R14B to [rdi+rdx]
   mov rax,rdi

   pop r15            ; Restore non-volatile registers
   pop r14
   ret

在这种情况下,我们使用易失性寄存器 RAX AL )和 RCX CL )进行交换。由于我们不必保留这些寄存器,因此无需保存和还原它们。

,

部分问题在于正在使用不可写内存区域进行写入,它将无法工作。 (asm还存在其他正确性问题,请参阅@MichaelPetch的答案。)

创建后:

char* st= "E X A M P L E";

因为它创建了string literal,所以指针st指向了不可写的内存位置。

如果创建为:

char st[] = "E X A M P L E";
存储在可写内存中的

st和它们的内容字符,而不只是持有指向只读字符串文字的指针。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?