Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/xpath/2.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++语言的理论构造函数以及它如何编译非常相似的方法到汇编程序(进一步深入二进制代码)的理论问题。在三个不同的方法中,汇编程序是否相似,每个方法都接受相同数量的参数,但提供相同的功能(可能是一个打印两个整数值的简单方法?)_C++_Assembly_Compiler Construction_Pass By Reference_Compiler Optimization - Fatal编程技术网

(理论)编译器和不同的传递类型 新序言 我对构造器理论不太了解,这是一个关于C++语言的理论构造函数以及它如何编译非常相似的方法到汇编程序(进一步深入二进制代码)的理论问题。在三个不同的方法中,汇编程序是否相似,每个方法都接受相同数量的参数,但提供相同的功能(可能是一个打印两个整数值的简单方法?)

(理论)编译器和不同的传递类型 新序言 我对构造器理论不太了解,这是一个关于C++语言的理论构造函数以及它如何编译非常相似的方法到汇编程序(进一步深入二进制代码)的理论问题。在三个不同的方法中,汇编程序是否相似,每个方法都接受相同数量的参数,但提供相同的功能(可能是一个打印两个整数值的简单方法?),c++,assembly,compiler-construction,pass-by-reference,compiler-optimization,C++,Assembly,Compiler Construction,Pass By Reference,Compiler Optimization,每个方法都以一对整数作为参数,但方式不同:一个方法通过值传递,另一个通过引用传递,最后一个通过地址传递。由于pass-by-reference和address变量必须作用于提供的内存位置的实际值,因此它们(除了访问值的代码之外)在编译版本中是否包含相同的代码 public void Foo (int a, int b) { std:cout << a << " " << b <<endl; } public void Bar (int* a

每个方法都以一对整数作为参数,但方式不同:一个方法通过值传递,另一个通过引用传递,最后一个通过地址传递。由于pass-by-reference和address变量必须作用于提供的内存位置的实际值,因此它们(除了访问值的代码之外)在编译版本中是否包含相同的代码

public void Foo (int a, int b)
{
   std:cout << a << " " << b <<endl;
}

public void Bar (int* a, int* b)
{
  // (aside from dereferencing code) the same code as Foo
}

public void FooBar (int& a, int& b)
{
  // again, the same code (fundamentally) here
}
假设此语言的编译器工作正常,没有bug,并且进行了充分的优化

在这样一个完美的系统中,三种方法的输出程序集和二进制代码是否都不匹配?如果不是,它们之间会有巨大的差异吗


澄清:假设这些方法非常简单,因为它们不会改变传入参数的值。

在完美系统中,是的

然而,这种优化水平相当极端
Bar
FooBar
几乎与
Foo
相同,但事实上它们几乎相同,这使得编译器很难检测到相似性,因为它基本上是对生成的代码进行区分,然后必须计算出差异有多大

如果您需要这种级别的优化,那么最好是这样编写代码

public void Foo(int a, int b)
{
  // Whatever
}

public void Bar (int* a, int* b)
{
  Foo(*a, *b);
}

public void FooBar (int& a, int& b)
{
  Foo(a, b);
}

<>现在编译器可以选择将代码> Foo为Bar > FooBar ,这是编译器做出的一个非常简单的优化决策。

< P>假定C++风格声明,Bar和FooBar将是相同的,除了调试信息,因为内部引用是指针,只有访问样式才能将它们与指针区分开来


对于第一个(Foo),如果不知道将来如何使用函数,则取决于您是否真的允许修改函数声明样式。如果它们的接口假设它们可以在指针下更改值(即使在非常极端的条件下),则不允许只传递值的优化。但是,假设经常调用函数,编译器和/或运行时可以将其更改为无指针的变量,并直接传递值。(但是,更可能的是,将应用一些其他优化,例如全功能内联。)

显然,如果代码确实修改了
a
b
,则前两个函数和后两个函数的语义完全不同;这显然会迫使编译器生成不同的代码;除此之外,如果这三个函数在给定程序中的每种情况下的行为都是相同的,那么编译器当然可以根据“仿佛”规则将它们简化为一个函数

但是这个问题没有多大意义——或者至少,在“完美系统”(不可能存在的东西)中询问二进制代码生成的具体差异(非常具体的实现细节)对我来说没有什么意义

回到真实系统,首先要考虑内联;如果函数是内联的,则它们与父函数的其余代码混合,并且每个扩展的输出可能不同(可以有不同的寄存器分配、不同的指令混合,以最大限度地提高管道利用率,…)

因此,比较应该是关于每个函数的“独立”输出

我希望第二个和第三个扩展到几乎完全相同的代码:引用是指针的语法糖,而
*NULL
引用不能被考虑在内,因为
*a
*b
可能在
(它告诉优化器假定它们永远不会是代码< null >代码>)。而且,我不知道区分指针和引用的任何C++ abi。< /P>
至于
Foo
,这将取决于许多因素:

  • 如果我们正在编译一个库,编译器不能自由地做它想做的任何事情,函数必须遵循某种ABI;因此,首先,在最后两种情况下,参数实际上是指针,在第一种情况下是值(根据平台ABI的不同,其结果会有所不同)
  • 如果编译器不能使用LTCG,并且我们希望使用来自其他模块的这些函数(即,这些函数没有标记为
    静态
    ),则这一点也适用
  • 在最后两种情况下,为了生成相同的输出,编译器可能需要证明引用/指针指向不同的值,以生成与
    Foo
    相同的输出
  • 此外,它必须能够证明
    a
    b
    在整个函数中没有改变;特别是,在每次外部(=非完全内联)函数调用之后,指向的对象可能已经改变;这两个都可能是复杂的任务,而且,如果程序由多个模块组成,它们可能需要LTCG
因此,我实际期望发生的是:

  • 对于独立版本,
    Foo!=Bar
    FooBar
    Bar==FooBar
  • 至于内联版本,编译器可能会有一个更简单的时间来确定将“
    Bar
    FooBar
    转换为相同语义的
    Foo
    ”的条件,但当然,生成的代码与不同函数的代码混合的事实将导致
    public void Foo(int a, int b)
    {
      // Whatever
    }
    
    public void Bar (int* a, int* b)
    {
      Foo(*a, *b);
    }
    
    public void FooBar (int& a, int& b)
    {
      Foo(a, b);
    }