C++ 程序运行周期期间代码块版本16.01崩溃

C++ 程序运行周期期间代码块版本16.01崩溃,c++,x86,g++,inline-assembly,C++,X86,G++,Inline Assembly,我有一个程序,它已经被证明可以在旧版本的代码块(13.12版)上运行,但在新版本(16.01版)上尝试时似乎不起作用。该计划的目的是输入两个整数,然后将被添加,mult等。它使用asm代码,我是新的。我的问题是,为什么它说在我输入2个整数并按enter键后windows停止响应 代码如下: //Program 16 #include <stdio.h> #include <iostream> using namespace std; int main() { i

我有一个程序,它已经被证明可以在旧版本的代码块(13.12版)上运行,但在新版本(16.01版)上尝试时似乎不起作用。该计划的目的是输入两个整数,然后将被添加,mult等。它使用asm代码,我是新的。我的问题是,为什么它说在我输入2个整数并按enter键后windows停止响应

代码如下:

//Program 16

#include <stdio.h>
 #include <iostream>
 using namespace std;

int main() {

int arg1, arg2, add, sub, mul, quo, rem ;

cout << "Enter two integer numbers : " ;
cin >>  arg1 >> arg2 ;
cout << endl;

  asm ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1) , "b" (arg2) );
  asm ( "subl %%ebx, %%eax;" : "=a" (sub) : "a" (arg1) , "b" (arg2) );
 asm ( "imull %%ebx, %%eax;" : "=a" (mul) : "a" (arg1) , "b" (arg2) );

asm ( "movl $0x0, %%edx;"
"movl %2, %%eax;"
"movl %3, %%ebx;"
"idivl %%ebx;" : "=a" (quo), "=d" (rem) : "g" (arg1), "g" (arg2) );

cout<< arg1 << "+" << arg2 << " = " << add << endl;
 cout<< arg1 << "-" << arg2 << " = " << sub << endl;
cout<< arg1 << "x" << arg2 << " = " << mul << endl;
cout<< arg1 << "/" << arg2 << " = " << quo << "  ";
 cout<< "remainder " << rem << endl;

return 0;
}
//程序16
#包括
#包括
使用名称空间std;
int main(){
内部arg1,arg2,添加,子,多,quo,rem;
cout>arg1>>arg2;

cout正如Michael所说,您的问题可能来自于您的第四个asm语句编写错误

编写内联asm时,首先需要了解寄存器是什么以及如何使用它们。寄存器是x86汇编程序编程中的一个基本概念,因此,如果您不知道它们是什么,那么是时候找到x86汇编语言入门了


一旦您了解了这一点,您需要了解,当编译器运行时,它会在生成的代码中使用这些寄存器。例如,如果您对(int x=0;xDavid Wohlferd给出了一个非常好的答案,说明了如何更好地使用GCC扩展程序集模板来完成现有代码的工作

可能会出现一个问题,即为什么代码块16.01 w/GCC出现故障,而之前可能是这样的。目前来看,代码看起来很简单,那么可能出了什么问题

我推荐的最好的方法是学习使用调试器并在代码块中设置断点。这非常简单(但超出了本答案的范围)。您可以在中了解有关调试的更多信息

如果使用调试器带有CODBROCK 16.01,使用一个股票C++控制台项目,您可能发现程序在装配模板中的IDEV指令中给了您一个算术异常。这是我的控制台输出中出现的:

程序收到信号SIGFPE,算术异常


这些代码行按照您的预期执行:

asm ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1) , "b" (arg2) );
asm ( "subl %%ebx, %%eax;" : "=a" (sub) : "a" (arg1) , "b" (arg2) );
asm ( "imull %%ebx, %%eax;" : "=a" (mul) : "a" (arg1) , "b" (arg2) );
这就是我们遇到的问题:

asm ( "movl $0x0, %%edx;"
      "movl %2, %%eax;"
      "movl %3, %%ebx;"
      "idivl %%ebx;" : "=a" (quo), "=d" (rem) : "g" (arg1), "g" (arg2) );
代码块可以为您做的一件事是显示它生成的汇编代码。下拉
Debug
菜单,选择
调试窗口>
反汇编
也会监视
CPU寄存器
我强烈推荐的窗口

如果您使用代码块16.01 w/GCC查看生成的代码,您可能会发现它生成了以下代码:

/* Automatically produced by the assembly template for input constraints */
mov    -0x20(%ebp),%eax      /* EAX = value of arg1 */
mov    -0x24(%ebp),%edx      /* EDX = value of arg2 */

/* Our assembly template instructions */
mov    $0x0,%edx             /* EDX = 0 - we just clobbered the previous EDX! */
mov    %eax,%eax             /* EAX remains the same */
mov    %edx,%ebx             /* EBX = EDX = 0. */
idiv   %ebx                  /* EBX is 0 so this is division by zero!! *

/* Automatically produced by the assembly template for output constraints */
mov    %eax,-0x18(%ebp)      /* Value at quo = EAX */
mov    %edx,-0x1c(%ebp)      /* Value at rem = EDX */
我已经对代码进行了注释,应该很清楚为什么这段代码不起作用。我们实际上在EBX中放置了零,然后尝试使用它作为IDIV的除数,这产生了一个算术异常(在本例中为零除)

之所以会出现这种情况,是因为GCC(默认情况下)将假定使用(使用)所有输入操作数在将输出操作数写入之前。我们从未告诉GCC它可能无法使用与输出操作数相同的输入操作数。GCC将这种情况视为一种错误。它提供了一种机制,使用
&
(与号)修饰符将输出约束标记为早期关闭:

`&"

表示(在特定替代方案中)此操作数是EarlyLobber操作数,在指令使用输入操作数完成之前对其进行修改。因此,此操作数不能位于用作输入操作数或任何内存地址一部分的寄存器中

通过更改操作数以便处理早期碰撞,我们可以将
&
放在两个输出约束上,如下所示:

"idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) );
"idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) : "ebx");
在这种情况下,
arg1
arg2
将不会通过标有
&
的任何操作数传入。这意味着此代码将避免对输入操作数
arg1
arg2
使用EAX和EDX

另一个问题是EBX被您的代码修改,但您没有告诉GCC。您可以简单地将EBX添加到程序集模板中的clobber列表中,如下所示:

"idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) );
"idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) : "ebx");
因此,此代码应该可以工作,但效率不高:

asm ( "movl $0x0, %%edx;"
      "movl %2, %%eax;"
      "movl %3, %%ebx;"
      "idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) : "ebx");
生成的代码现在看起来像:

/* Automatically produced by the assembler template for input constraints */
mov    -0x30(%ebp),%ecx      /* ECX = value of arg1 */
mov    -0x34(%ebp),%esi      /* ESI = value of arg2 */

/* Our assembly template instructions */
mov    $0x0,%edx             /* EDX = 0 */
mov    %ecx,%eax             /* EAX = ECX = arg1 */
mov    %esi,%ebx             /* EBX = ESI = arg2 */
idiv   %ebx

/* Automatically produced by the assembler template for output constraints */
mov    %eax,-0x28(%ebp)      /* Value at quo = EAX */
mov    %edx,-0x2c(%ebp)      /* Value at rem = EDX */
这次,
arg1
arg2
的输入操作数没有共享与内联程序集模板中的
MOV
指令冲突的相同寄存器


为什么其他(包括旧版本)的GCC可以工作? 如果GCC使用EAX、EDX和EBX以外的寄存器为
arg1
arg2
操作数生成指令,那么它就可以工作了。但它可能工作只是运气好。要查看旧代码块及其附带的GCC发生了什么,我建议查看在该环境中生成的代码他和我上面讨论的一样

一般来说,早期删除和寄存器删除是扩展汇编程序模板可能比较棘手的一个原因,也是应该谨慎使用扩展汇编程序模板的一个原因,特别是如果您对扩展汇编程序模板的理解不够透彻的话

您可以创建看似有效但编码不正确的代码。不同版本的GCC甚至不同的优化级别可能会改变代码的行为。有时,这些错误可能非常微妙,以至于随着程序的增长,错误会以其他难以跟踪的方式表现出来


另一条经验法则是,并非您在internet上找到的所有代码都是无缺陷的,并且在教程中经常忽略扩展内联汇编的微妙复杂性。我发现您使用的代码似乎基于此。不幸的是,作者没有对所涉及的内部问题有透彻的理解。这些代码可能在是时候了,但不一定是现在。

解决此类问题的正确工具是调试器。在询问堆栈溢出问题之前,您应该逐行检查代码。有关更多帮助,请阅读。至少,您应该[编辑]您的问题以包括
asm ( "movl $0x0, %%edx;"
      "movl %2, %%eax;"
      "movl %3, %%ebx;"
      "idivl %%ebx;" : "=a" (quo), "=d" (rem) : "g" (arg1), "g" (arg2) );
/* Automatically produced by the assembly template for input constraints */
mov    -0x20(%ebp),%eax      /* EAX = value of arg1 */
mov    -0x24(%ebp),%edx      /* EDX = value of arg2 */

/* Our assembly template instructions */
mov    $0x0,%edx             /* EDX = 0 - we just clobbered the previous EDX! */
mov    %eax,%eax             /* EAX remains the same */
mov    %edx,%ebx             /* EBX = EDX = 0. */
idiv   %ebx                  /* EBX is 0 so this is division by zero!! *

/* Automatically produced by the assembly template for output constraints */
mov    %eax,-0x18(%ebp)      /* Value at quo = EAX */
mov    %edx,-0x1c(%ebp)      /* Value at rem = EDX */
"idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) );
"idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) : "ebx");
asm ( "movl $0x0, %%edx;"
      "movl %2, %%eax;"
      "movl %3, %%ebx;"
      "idivl %%ebx;" : "=&a" (quo), "=&d" (rem) : "g" (arg1), "g" (arg2) : "ebx");
/* Automatically produced by the assembler template for input constraints */
mov    -0x30(%ebp),%ecx      /* ECX = value of arg1 */
mov    -0x34(%ebp),%esi      /* ESI = value of arg2 */

/* Our assembly template instructions */
mov    $0x0,%edx             /* EDX = 0 */
mov    %ecx,%eax             /* EAX = ECX = arg1 */
mov    %esi,%ebx             /* EBX = ESI = arg2 */
idiv   %ebx

/* Automatically produced by the assembler template for output constraints */
mov    %eax,-0x28(%ebp)      /* Value at quo = EAX */
mov    %edx,-0x2c(%ebp)      /* Value at rem = EDX */