如何解决带一个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)
如果您将index1
和index2
保留为有符号整数(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 int
和index
使用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是:
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 举报,一经查实,本站将立刻删除。