Я новичок в С++ 11, поэтому возникают подобные вопросы))
Собственно, имеем такой код:
struct testStruct
{
int x = 10;
int y = 11;
int z = 12;
};
testStruct foo()
{
return testStruct();
}
int main(int _argc, char* _argv[])
{
testStruct&& rv_ref = foo();
rv_ref.z = 254;
return 0;
}
Из которого мы при дизассемблировании получаем такой машинный код:
int main(int _argc, char* _argv[])
{
00007FF6996114F0 mov qword ptr [rsp+10h],rdx
00007FF6996114F5 mov dword ptr [rsp+8],ecx
00007FF6996114F9 push rsi
00007FF6996114FA push rdi
00007FF6996114FB sub rsp,78h
00007FF6996114FF mov rdi,rsp
00007FF699611502 mov ecx,1Eh
00007FF699611507 mov eax,0CCCCCCCCh
00007FF69961150C rep stos dword ptr [rdi]
00007FF69961150E mov ecx,dword ptr [_argc]
testStruct&& rv_ref = foo();
00007FF699611515 lea rcx,[rsp+60h]
00007FF69961151A call foo (07FF69961124Eh)
00007FF69961151F lea rcx,[rsp+54h]
00007FF699611524 mov rdi,rcx
00007FF699611527 mov rsi,rax
00007FF69961152A mov ecx,0Ch
00007FF69961152F rep movs byte ptr [rdi],byte ptr [rsi]
00007FF699611531 lea rax,[rsp+38h]
00007FF699611536 lea rcx,[rsp+54h]
00007FF69961153B mov rdi,rax
00007FF69961153E mov rsi,rcx
00007FF699611541 mov ecx,0Ch
00007FF699611546 rep movs byte ptr [rdi],byte ptr [rsi]
00007FF699611548 lea rax,[rsp+38h]
00007FF69961154D mov qword ptr [rv_ref],rax
rv_ref.z = 254;
00007FF699611552 mov rax,qword ptr [rv_ref]
00007FF699611557 mov dword ptr [rax+8],0FEh
return 0;
00007FF69961155E xor eax,eax
}
Обратите внимание на строки
00007FF69961152F rep movs byte ptr [rdi],byte ptr [rsi]
и
00007FF699611546 rep movs byte ptr [rdi],byte ptr [rsi]
Они производят копирование данных из одного куска памяти на стеке, в другой кусок памяти на стеке.
Так вот, зачем возвращаемое из функции foo() значение копируется на стек вызывающей функции main() ЦЕЛЫХ 2 РАЗА???
По логике, хватило бы даже одного того участка стекового кадра, который использовался для возврата значения из функции foo(). Однако же в сгенерированном машинном коде, мы видим целых две копии возвращаемого значения!
Что интересно, если я даже уберу из кода ссылку на r-value, вот так:
int main(int _argc, char* _argv[])
{
foo();
return 0;
}
То в сгенерированном машинном коде все равно останется копирование возвращаемого значения на стек вызывающей функции!
int main(int _argc, char* _argv[])
{
00007FF6747F14D0 mov qword ptr [rsp+10h],rdx
00007FF6747F14D5 mov dword ptr [rsp+8],ecx
00007FF6747F14D9 push rsi
00007FF6747F14DA push rdi
00007FF6747F14DB sub rsp,48h
00007FF6747F14DF mov rdi,rsp
00007FF6747F14E2 mov ecx,12h
00007FF6747F14E7 mov eax,0CCCCCCCCh
00007FF6747F14EC rep stos dword ptr [rdi]
00007FF6747F14EE mov ecx,dword ptr [_argc]
foo(127);
00007FF6747F14F2 mov edx,7Fh
00007FF6747F14F7 lea rcx,[rsp+30h]
00007FF6747F14FC call foo (07FF6747F114Fh)
00007FF6747F1501 lea rcx,[rsp+20h]
00007FF6747F1506 mov rdi,rcx
00007FF6747F1509 mov rsi,rax
00007FF6747F150C mov ecx,0Ch
00007FF6747F1511 rep movs byte ptr [rdi],byte ptr [rsi]
return 0;
00007FF6747F1513 xor eax,eax
}
При чем в некоторых случаях сохраняется двойное копирование, как в предыдущем листинге (в этот раз я просто не поймал этот дизассемблер, от чего зависит количество копирований возвращаемого значения, не знаю)
Так вот, зачем делать столько копий возвращаемого значения на стеке вызывающей функции???
Является ли это всего лишь следствием того, что современные компиляторы, как и многие другие программные продукты являются дикой, многоуровневой матрешкой и разработчики, напрмер, некоего парсера возвращаемых значений функции (не ругайте меня, нуба, если что, за подобные выражения)) совсем не в курсах о том как происходит генерация машинного кода, потому что для них это уже 100500-я абстракция. Или же это имеет некий пока непонятный для меня, сакральный смысл, реализованный в колдунстве со всеми этими r-value, x-value, const и прочей мишурой нового с++ 11, обеспечивающий, например, некие неизменяемые копии, мувы и т.д?!
Буду очень благодарен за пояснения!
А это ничего, что VC++17 при компиляции дает
main PROC ; COMDAT
; 22 : testStruct&& rv_ref = foo();
; 23 : rv_ref.z = 254;
; 24 : return 0;
xor eax, eax
; 25 : }
ret 0
main ENDP
? :)
Выходной ассемблерный код может быть... ну, несколько различным, так что...
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
В С++ для атрибутов класса можно задавать свойства, шаблон определения которых -
Пытаюсь в конструкторе B вызвать слот A::toExit()Как исправить ошибку?
Изначально есть бинарный файлМне полностью известна его структура