- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个动态分配的 2d int 数组,称为 image,还有一个格式字符串称为 format。然后我使用两个嵌套的 for 循环从标准输入中获取输入,并将它们存储在二维数组中。所以我可以动态地解析来自不同长度的输入的整数。例如,如果我有一个 3x3 二维数组,我将需要使用内联汇编将数组中的元素地址推送 9 次,然后推送到格式字符串。然后我调用 scanf,并在完成时平衡堆栈。
顺便说一句:假定数组的宽度和高度已知。
这里是我在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"
);
}
执行此代码时出现段错误。会出什么问题?谢谢!
最佳答案
Linux 上的 x86_64 代码使用 a completely different calling convention比 Windows 上的 x86 代码。特别是,它会在开始使用堆栈之前尝试在寄存器中传递许多参数。此外,可变参数有一些微妙的额外规则(例如,您必须在 rax
中指定实际使用的 XMM 寄存器的数量,如果没有使用则为 0)。
scanf
期望在寄存器中找到前六个指针参数,但您将它们放在堆栈中,并且寄存器包含垃圾值(无论调用时碰巧在那里);当取消引用其中任何一个以写入读取值时,您会遇到段错误。
此外,现代编译器通常不使用rbp
作为帧指针来访问局部变量和参数,帧指针被省略,通过rsp
访问局部变量。随着你的推送,你在编译器不知道的情况下移动了堆栈指针,现在你的推送和函数调用返回之间的每个堆栈访问都将被破坏。你hand try to hand-hold the compiler around this ,但这是一件肮脏的事情,而且容易崩溃。
更糟糕的是:如果 gcc 认为您的函数是一个叶函数(如果唯一的函数调用在您的汇编代码中,它可能会这么认为,这对编译器是不透明的)它可能正在利用 red zone , 将内容置于 rsp
的当前值之下。您的推送和函数调用可能会覆盖此数据。你can try to fight even this ,但同样,这是丑陋的东西。
所以:很明显为什么你的代码不起作用,而且很明显要让它在 x86_64 调用约定上正确工作是相当复杂的 - 你必须将东西放在不同的寄存器或堆栈上,具体取决于迭代,并找到一种方法告诉 gcc 你正在弄乱堆栈指针并避免使用红色区域。
我不清楚的是:这件事有什么意义?如果您需要读取许多值,如果值的数量是固定的,您可以在纯 C 中执行 scanf
的“正常”调用。相反,如果要读取的值的数量仅在运行时已知,从您的评论看来,
It is like
"%d %d %d ...."
, and varies its length dynamically.
只需多次调用 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
的可变调用是一个糟糕的、不可移植的解决方案来寻找问题。
关于在 X64 gcc 内联 asm 中调用 scanf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49723621/
为了让我的代码几乎完全用 Jquery 编写,我想用 Jquery 重写 AJAX 调用。 这是从网页到 Tomcat servlet 的调用。 我目前情况的类似代码: var http = new
我想使用 JNI 从 Java 调用 C 函数。在 C 函数中,我想创建一个 JVM 并调用一些 Java 对象。当我尝试创建 JVM 时,JNI_CreateJavaVM 返回 -1。 所以,我想知
环顾四周,我发现从 HTML 调用 Javascript 函数的最佳方法是将函数本身放在 HTML 中,而不是外部 Javascript 文件。所以我一直在网上四处寻找,找到了一些简短的教程,我可以根
我有这个组件: import {Component} from 'angular2/core'; import {UserServices} from '../services/UserService
我正在尝试用 C 实现一个简单的 OpenSSL 客户端/服务器模型,并且对 BIO_* 调用的使用感到好奇,与原始 SSL_* 调用相比,它允许一些不错的功能。 我对此比较陌生,所以我可能会完全错误
我正在处理有关异步调用的难题: 一个 JQuery 函数在用户点击时执行,然后调用一个 php 文件来检查用户输入是否与数据库中已有的信息重叠。如果是这样,则应提示用户确认是否要继续或取消,如果他单击
我有以下类(class)。 public Task { public static Task getInstance(String taskName) { return new
嘿,我正在构建一个小游戏,我正在通过制作一个数字 vector 来创建关卡,该数字 vector 通过枚举与 1-4 种颜色相关联。问题是循环(在 Simon::loadChallenge 中)我将颜
我有一个java spring boot api(数据接收器),客户端调用它来保存一些数据。一旦我完成了数据的持久化,我想进行另一个 api 调用(应该处理持久化的数据 - 数据聚合器),它应该自行异
首先,这涉及桌面应用程序而不是 ASP .Net 应用程序。 我已经为我的项目添加了一个 Web 引用,并构建了各种数据对象,例如 PayerInfo、Address 和 CreditCard。但问题
我如何告诉 FAKE 编译 .fs文件使用 fsc ? 解释如何传递参数的奖励积分,如 -a和 -target:dll . 编辑:我应该澄清一下,我正在尝试在没有 MSBuild/xbuild/.sl
我使用下划线模板配置了一个简单的主干模型和 View 。两个单独的 API 使用完全相同的配置。 API 1 按预期工作。 要重现该问题,请注释掉 API 1 的 URL,并取消注释 API 2 的
我不确定什么是更好的做法或更现实的做法。我希望从头开始创建目录系统,但不确定最佳方法是什么。 我想我在需要显示信息时使用对象,例如 info.php?id=100。有这样的代码用于显示 Game.cl
from datetime import timedelta class A: def __abs__(self): return -self class B1(A):
我在操作此生命游戏示例代码中的数组时遇到问题。 情况: “生命游戏”是约翰·康威发明的一种细胞自动化技术。它由一个细胞网格组成,这些细胞可以根据数学规则生存/死亡/繁殖。该网格中的活细胞和死细胞通过
如果我像这样调用 read() 来读取文件: unsigned char buf[512]; memset(buf, 0, sizeof(unsigned char) * 512); int fd;
我用 C 编写了一个简单的服务器,并希望调用它的功能与调用其他 C 守护程序的功能相同(例如使用 ./ftpd start 调用它并使用 ./ftpd stop 关闭该实例)。显然我遇到的问题是我不知
在 dos 中,当我粘贴此命令时它会起作用: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://google.
在 dos 中,当我粘贴此命令时它会起作用: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://google.
我希望能够从 cmd 在我的 Windows 10 计算机上调用 python3。 我已重新安装 Python3.7 以确保选择“添加到路径”选项,但仍无法调用 python3 并使 CMD 启动 P
我是一名优秀的程序员,十分优秀!