Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++;CPU寄存器使用率 在C++中,局部变量总是在堆栈上分配。堆栈是应用程序可以占用的允许内存的一部分。该内存保存在RAM中(如果没有交换到磁盘)。现在,C++编译器是否总是创建在堆栈上存储局部变量的汇编代码?_C++_Compiler Construction_Assembly_Mips_Cpu Registers - Fatal编程技术网

C++;CPU寄存器使用率 在C++中,局部变量总是在堆栈上分配。堆栈是应用程序可以占用的允许内存的一部分。该内存保存在RAM中(如果没有交换到磁盘)。现在,C++编译器是否总是创建在堆栈上存储局部变量的汇编代码?

C++;CPU寄存器使用率 在C++中,局部变量总是在堆栈上分配。堆栈是应用程序可以占用的允许内存的一部分。该内存保存在RAM中(如果没有交换到磁盘)。现在,C++编译器是否总是创建在堆栈上存储局部变量的汇编代码?,c++,compiler-construction,assembly,mips,cpu-registers,C++,Compiler Construction,Assembly,Mips,Cpu Registers,以以下简单代码为例: int foo( int n ) { return ++n; } 在MIPS汇编程序代码中,这可能如下所示: foo: addi $v0, $a0, 1 jr $ra 如您所见,我根本不需要为n使用堆栈。C++编译器会识别出,并且直接使用CPU的寄存器吗? 编辑:哇,非常感谢您几乎立即给出的广泛答案!foo的函数体当然应该是return++n,而不是返回n++:) 是的。没有“变量总是在堆栈上分配”的规则。C++标准不涉及堆栈。它不假定栈存在,或者寄存器存在。它只

以以下简单代码为例:

int foo( int n ) {
   return ++n;
}
在MIPS汇编程序代码中,这可能如下所示:

foo:
addi $v0, $a0, 1
jr $ra
如您所见,我根本不需要为n使用堆栈。C++编译器会识别出,并且直接使用CPU的寄存器吗?
编辑:哇,非常感谢您几乎立即给出的广泛答案!foo的函数体当然应该是
return++n
,而不是
返回n++:)

是的。没有“变量总是在堆栈上分配”的规则。C++标准不涉及堆栈。它不假定栈存在,或者寄存器存在。它只是说明代码应该如何运行,而不是如何实现

编译器仅在必要时将变量存储在堆栈上-例如,当它们必须通过函数调用时,或者如果您试图获取它们的地址时


编译器并不愚蠢

是的,一个好的、优化的C/C++会优化它。甚至更多:

一个普通的C/C++编译器无论如何也不会把所有变量都放在堆栈上。
foo()
函数的问题可能是变量可能通过堆栈传递给函数(系统(硬件/操作系统)的ABI定义了这一点)

使用C的
register
关键字,您可以给编译器一个提示,在寄存器中存储一个变量可能会更好。样本:

register int x = 10;

但是请记住:如果编译器愿意,它可以自由地不在寄存器中存储
x

免责声明:我不知道MIPS,但我知道一些x86,我认为原理应该是一样的

在通常的函数调用约定中,编译器将
n
的值推送到堆栈上,以将其传递给函数
foo
。但是,有一种
fastcall
约定,您可以使用它来告诉gcc通过寄存器传递值。(MSVC也有这个选项,但我不确定它的语法是什么。)

test.cpp:

int foo1 (int n) { return ++n; }
int foo2 (int n) __attribute__((fastcall));
int foo2 (int n) {
    return ++n;
}
g++-O3-fomit frame pointer-c test.cpp编译上面的代码,我得到了
foo1

mov eax,DWORD PTR [esp+0x4]
add eax,0x1
ret
如您所见,它从堆栈中读取值

下面是
foo2

lea eax,[ecx+0x1]
ret
现在它直接从寄存器中获取值

当然,如果您内联函数,编译器将在较大的函数体中进行简单的添加,而不管您指定的调用约定如何。但是当你不能把它内联起来时,这种情况就会发生

免责声明2:我不是说你应该不断地猜测编译器。在大多数情况下,它可能不实用,也不必要。但不要认为它能产生完美的代码

编辑1:如果您谈论的是纯局部变量(不是函数参数),那么是的,编译器将根据需要在寄存器或堆栈中分配它们


编辑2:调用约定似乎是特定于体系结构的,MIPS将传递堆栈上的前四个参数,正如Richard Pennington在其回答中所述。因此,在您的情况下,您不必指定额外的属性(实际上是x86特定的属性)。

答案可能是肯定的。它取决于编译器、优化级别和目标处理器

在mips的情况下,前四个参数(如果很小)在寄存器中传递,返回值在寄存器中返回。因此,您的示例不需要在堆栈上分配任何内容

事实上,真相比小说更离奇。在您的情况下,参数返回不变:返回的值是++运算符前面的n值:

foo:
    .frame  $sp,0,$ra
    .mask   0x00000000,0
    .fmask  0x00000000,0

    addu    $2, $zero, $4
    jr      $ra
    nop

因为你的例子<代码> Foo> /Cuff>函数是一个身份函数(它只返回它的参数),我的C++编译器(VS 2008)完全删除了这个函数调用。如果我将其更改为:

int foo( int n ) {
   return ++n;
}
编译器将它与

lea edx, [eax+1] 

<是的,寄存器是用C++来实现的。MDR(内存数据寄存器)包含正在获取和存储的数据。例如,为了检索单元格123的内容,我们将把值123(二进制)加载到MAR中,并执行一个fetch操作。操作完成后,单元格123内容的副本将位于MDR中。为了将值98存储到单元格4中,我们将4加载到MAR中,将98加载到MDR中并执行存储。当操作完成时,单元格4的内容将被设置为98,方法是丢弃以前存在的内容。数据和地址寄存器与它们一起工作以实现这一点。在C++中,当我们用值初始化var或询问其值时,同样的现象也会发生。
此外,现代编译器还执行寄存器分配,这比内存分配要快

是的,同样使用mips示例:静态int-foo(int-n){return n++;}int-fee(){return-foo(5);}给出:.text.align 2.globl-fee.ent-fee:.frame$sp,0,$ra.mask 0x00000000,0.fmask 0x00000000,0 addiu$2,$0,5 jr$ra nop.set macro.set reorder.end fee.size fee,.-fee-O在不干扰调试的计算机上禁用堆栈帧设置-x86不是其中之一,您需要一个单独的-fomit帧指针来消除“冗余”堆栈帧设置(这实际上对调试很有用,即在堆栈帧展开中)是的,我完全忘记了。我会修复它。但区别仍然存在。进行链接时间优化的编译器也可以识别调用可以自己转换为快速调用,因为它可以