Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.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++ 返回值常量&;和常量赋值-disassembly_C++_Disassembly - Fatal编程技术网

C++ 返回值常量&;和常量赋值-disassembly

C++ 返回值常量&;和常量赋值-disassembly,c++,disassembly,C++,Disassembly,最近我一直在分析旧代码的某些部分,在某些情况下,函数返回的值被分配给const变量,有时分配给const&。出于好奇,我换成了dissasembly,看看其中的区别。但在说到重点之前,让我先画一个简单的示例,其中有一些代码可供参考: struct Data { int chunk[1024]; }; Data getData() { return Data(); } int main() { const std::string varInit{ "abc" }; //

最近我一直在分析旧代码的某些部分,在某些情况下,函数返回的值被分配给
const
变量,有时分配给
const&
。出于好奇,我换成了dissasembly,看看其中的区别。但在说到重点之前,让我先画一个简单的示例,其中有一些代码可供参考:

struct Data
{
    int chunk[1024];
};

Data getData()
{
    return Data();
}

int main()
{
    const std::string varInit{ "abc" }; // 1
    const std::string varVal = "abc"; // 2
    const std::string& varRef = "abc"; // 3

    const Data dataVal = getData(); // 4
    const Data& dataRef = getData(); // 5

    return 0;
}
以下对上述代码的反汇编是在禁用优化的情况下使用VS2015获得的

我不是asm专家,但乍一看,我认为对于
(1)
(2)
有类似的操作。然而,与以前的版本相比,
const&
在变量值赋值过程中没有使用,
lea
mov
),我很惊讶
(3)
携带了两个额外的操作(
lea
mov

当数据按值从函数返回时,也可以观察到同样的情况
(5)
执行与
(4)
相关的另外两个操作。

问题很窄:

  • 这些额外的操作从何而来?它们的目的是什么?一般来说不像这里:而是在呈现的上下文中
  • 至少对于基线数据大小可以忽略不计的对象,这是否会影响性能?(与示例中使用的
    Data
    struct相反)
  • 当优化打开时,这会有任何影响吗?(用于发布版本)
顺便说一句,我已经读过使用const和const的优缺点&当赋值时,它们可能有些关联,但不是问题的一部分。

在案例(3)中,编译器在
[ebp-84]
处创建“隐藏的”局部变量std::string”,让它命名为
\u 84
,然后这样编码

const std::string _84 = "abc"; 
const std::string& varRef = _84;// lea + move (by sense varRef = &_84)
void InitData(Data* p);

Data dataVal;
InitData(&dataVal);
X*v
-
v
相同的
X&v
-在这两种情况下实际上都指向
X
,只是使用了不同的语法

与第(5)种情况相同

或者说如果你改为排队

const Data& dataRef = getData();
写行

const Data& dataRef = dataVal;
您认为此行正好包含两条asm指令:

lea eax,[dataVal]
mov [dataRef],eax
代码(4,5)和
datagetdata()
signature绝对是噩梦,没有文字


为了更清楚 关于返回结构“按值”-函数只能返回寄存器作为结果(
al
ax
eax
rax
在x64中)或2个寄存器-
edx:eax
(8字节,edx在高位)或
rdx:rax
(x64中为16字节)

万一
Data getData()
-无法按原样返回
Data
。怎么会

所以实际上你的函数被转化为

Data* getData(Data* p)
{
    Data x;
    memcpy(p, &x, sizeof(Data));
    return p;
}
和代码

//const Data dataVal = getData(); 
Data _41A0, _3198, dataVal;
memcpy(&_3198, getData(&_41A0), sizeof(Data));
memcpy(&dataVal, &_3198, sizeof(Data));

//const Data& dataRef = getData(); 
Data _41A0, _3198, _20a0, &dataRef;
memcpy(&_51a8, getData(&_61b0), sizeof(Data));
memcpy(&_20a0, &_51a8, sizeof(Data));
dataRef = &_20a0;// very small influence compare all other
试算一下,编译器有多少无意义的memcpy

需要这样写代码吗

const std::string _84 = "abc"; 
const std::string& varRef = _84;// lea + move (by sense varRef = &_84)
void InitData(Data* p);

Data dataVal;
InitData(&dataVal);
在案例(3)中,编译器在
[ebp-84]
处创建“隐藏的”局部变量“std::string”,并将其命名为
\u 84
,然后执行如下代码

const std::string _84 = "abc"; 
const std::string& varRef = _84;// lea + move (by sense varRef = &_84)
void InitData(Data* p);

Data dataVal;
InitData(&dataVal);
X*v
-
v
相同的
X&v
-在这两种情况下实际上都指向
X
,只是使用了不同的语法

与第(5)种情况相同

或者说如果你改为排队

const Data& dataRef = getData();
写行

const Data& dataRef = dataVal;
您认为此行正好包含两条asm指令:

lea eax,[dataVal]
mov [dataRef],eax
代码(4,5)和
datagetdata()
signature绝对是噩梦,没有文字


为了更清楚 关于返回结构“按值”-函数只能返回寄存器作为结果(
al
ax
eax
rax
在x64中)或2个寄存器-
edx:eax
(8字节,edx在高位)或
rdx:rax
(x64中为16字节)

万一
Data getData()
-无法按原样返回
Data
。怎么会

所以实际上你的函数被转化为

Data* getData(Data* p)
{
    Data x;
    memcpy(p, &x, sizeof(Data));
    return p;
}
和代码

//const Data dataVal = getData(); 
Data _41A0, _3198, dataVal;
memcpy(&_3198, getData(&_41A0), sizeof(Data));
memcpy(&dataVal, &_3198, sizeof(Data));

//const Data& dataRef = getData(); 
Data _41A0, _3198, _20a0, &dataRef;
memcpy(&_51a8, getData(&_61b0), sizeof(Data));
memcpy(&_20a0, &_51a8, sizeof(Data));
dataRef = &_20a0;// very small influence compare all other
试算一下,编译器有多少无意义的memcpy

需要这样写代码吗

const std::string _84 = "abc"; 
const std::string& varRef = _84;// lea + move (by sense varRef = &_84)
void InitData(Data* p);

Data dataVal;
InitData(&dataVal);

编译器将
varRef
引用实现为普通的“伪装指针”。
lea
指令计算该指针的初始值(
ecx=ebp-84h
),而
mov
指令将该值保存到
varRef
指针中。编译器将
varRef
引用实现为普通的“伪装指针”。
lea
指令计算该指针的初始值(
ecx=ebp-84h
),而
mov
指令将该值保存到
varRef
指针中当您可以打开优化并让编译器在有利的时候为您进行转换时。见鬼,我无法说服g++不要在-O1以上的任何东西上内联调用
getData
,甚至在RVO介入并在适当的位置构造对象。@MilesBudnek-是的,现代编译器可以严重优化未优化的源代码。但是,最好的选择是——了解我们在做什么,了解我们在做什么——然后开始编写更好的代码。函数返回的值不可能超过2个寄存器大小。确实需要将指针传递到数据。最好的代码是易于阅读的代码<代码>数据dataVal=getData()清楚地表达了程序员的意图,编译器将为其生成相同的目标代码或
InitData(&dataVal)。即使是on-o0g++也会为这两个表达式生成几乎完全相同的代码。我同意知道引擎盖下发生了什么很好,但是知道编译器真的很擅长它的工作也很重要。谢谢,很好的“幕后”解释。我已经检查了const Data&dataRefFromVal=dataVal它实际上只接受上面提到的2条指令-这就是我所缺少的。在我看来,在RVO和复制省略时代,通常使用
const&
而变量赋值在