我有一个动态分配的2d int数组,称为image,以及一个名为format的格式字符串。 然后我使用两个嵌套for循环来获取标准输入的输入,并将它们存储在2d数组中。因此,我可以动态地解析来自不同长度的输入的整数。 例如,如果我有一个3x3 2d数组,我将需要使用内联asm来推送数组中的元素地址9次,并推送到格式字符串。然后我调用scanf,并在完成后平衡堆栈。
BTW:假设数组的宽度和高度已经知道。
这是我在Windows上的代码(X64系统,用x32代码编译)。它工作正常。
for (int i = 0; i < height; i++) {
for (int j = width-1; j >=0; j--) {
int tmp_addr = (int)&image[i][j];
__asm push tmp_addr;
}
int pop_size = (width+1) * 4;
__asm {
push format;
call func_scanf;
mov read_size, eax;
add esp, pop_size;
}
}
当我将它移植到Linux(X64系统,用X64代码编译)时,代码不起作用。
for (int i = 0; i < height; i++) {
for (int j = width-1; j >=0; j--) {
long tmp_addr = (long)&image[i][j];
//__asm push tmp_addr;
__asm__ __volatile__(
"push %0\n\t"
::"g"(tmp_addr)
);
}
int pop_size = (width+1) * sizeof(long);
/*__asm {
push format;
call func_scanf;
mov read_size, eax;
add esp, pop_size;
}*/
__asm__ __volatile__(
"push %0\n\t"
"call *%1\n\t"
"mov %%rax,%2\n\t"
"add %3,%%rsp"
::"g"(format),"g"(func_scanf),"g"(read_size),"g"(pop_size)
:"%rax","%rsp"
);
}
执行此代码时,错误显示Segmentation fault。 怎么可能出错? 谢谢!
答案 0 :(得分:0)
rax
中指定实际使用的XMM寄存器的数量,如果没有使用则为0)。
scanf
期望在寄存器中找到前六个指针参数,但是你将它们放在堆栈中,并且寄存器包含垃圾值(无论在调用时发生什么);当解除引用其中任何一个来写入读取值时,您会得到一个段错误。
此外,现代编译器通常不使用rbp
作为帧指针来访问本地和参数,省略帧指针并通过rsp
访问本地。通过推送,您可以在编译器不知情的情况下移动堆栈指针,现在您的推送和函数调用返回之间的每个堆栈访问都将被破坏。你hand try to hand-hold the compiler around this,但这是肮脏的生意,容易破产。
更糟糕的是:如果gcc认为你的函数是一个leaf函数(并且如果唯一的函数调用在你的汇编代码中,对编译器是不透明的,它可能会这样认为)它可能正在利用red zone,把东西放在rsp
的当前值之下。您的推送和函数调用可能会覆盖此数据。你can try to fight even this,但又一次,它是丑陋的东西。
所以:很清楚为什么你的代码不起作用,而且很清楚,在x86_64调用约定上使它正常工作非常复杂 - 你&# 39; d必须根据迭代将东西放在不同的寄存器或堆栈中,并找到告诉gcc你正在弄乱堆栈指针并避免使用红色区域的方法。
我不清楚的是:这件事的重点是什么?如果你需要读取很多值,如果值的数量是固定的,你可以做一个&#34; normal&#34;在简单C中调用scanf
。相反,只有在运行时才知道要读取的值的数量,如您的注释所示,
就像
"%d %d %d ...."
一样,并动态地改变它的长度。
只需多次scanf
调用一个适合读取单个值的格式字符串:
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
scanf("%d", &image[i][j]);
}
}
这将与您的代码(在其工作的平台上)具有完全相同的语义。顺便说一下,添加一些错误处理(=检查scanf
的返回值),您的程序将在遇到无效值时停止读取,并在image
中继续使用未初始化的值。
如果性能有问题,只需抛弃scanf
- 无论如何都可以通过手动编写令牌化代码然后调用strtol
轻松击败它;通过手动编写转换代码,您甚至可以比strtol
(如果您不关心语言环境)更快。
在任何情况下,下载到汇编级别以构建对scanf
的可变参数调用是一种可怕的,不可移植的解决问题的解决方案。