簡體   English   中英

右值如何分配給匯編中的左值?

[英]How are rvalues assigned to lvalues in assembly?

這里的第一個問題。 我將在幾周/幾個月內創建程序代碼,其中將有函數將大(我的意思是非常大)數據集直接分配給指針。 這是我將要做的一些代碼示例:

void MyFuntion(string* str)
{
     *str = "some data in a string";
}

As it surely is important: I am on windows 10, in visual-studio 2019, compiling with the default c++ compiler on release x86.

想象一下這樣的情況,但字符串可以包含數百萬個字符,或者 int/float arrays 也具有數百萬個元素。 因此,這是將右值分配給指針的單個操作,因此該指針位於堆上。 當然,如果我創建一個包含數據的局部變量,它會超過 1MB,因此會導致堆棧溢出,對嗎?

據我了解,由於數據在這里僅作為右值存在,因此它不存在 memory ,但我想知道:右值是如何分配給指針的? 就像,它是如何在組裝中完成的? 我必須說我從未做過任何組裝,我有一些(很少)想法,但我想在有時間的時候進入它。

它是在放入最終的 memory 地址之前在堆棧或堆中臨時創建的嗎? 我的猜測是 memory 地址(我在其中分配數據的指針)直接填充了數據,例如,逐位填充,因此 memory 中不存在右值。

如果我是正確的,這里堆棧中唯一存在的東西是:function 調用,指針副本,然后是指令,應該類似於“將右值 X 分配給左值 Y”,並且指令的大小不會t 取決於右值和左值的大小,所以這里的堆棧應該沒有任何問題。

所以,如果我是正確的,這段代碼應該不會引起任何問題,不管右值有多大,但我仍然想知道它是如何准確地完成的,組裝方式。 請注意,我不僅在尋找答案,而且更像是一些可以詳細解釋的參考資料、書籍或文檔。 我想我正在尋找的內容不會出現在 c++ 書中,但更像是一本匯編書,這可能是讓自己進入其中的一個很好的起點!

盡管提到了特定的操作系統和編譯器,但此答案中的示例程序集可能與查詢者的編譯器 output 不同,因為在撰寫本文時我沒有可用的 Windows 10 機器,並且使用了忘記了的不同環境神箭 但是,我認為這個主題足夠籠統,在這種特定情況下並不重要。


賦值運算符右側的值是什么? 裝配級別的分配是什么樣的? 這是一個簡單的例子。

void assign_thing(int *p) {
    *p = 42;
}
movl $42, (%rdi)
retq

“將 32 位 integer 42移動到rdi指向的 memory 位置。” %rdi這里代表p(%rdi)代表*p 對於像 integer 這樣簡單的東西,就這么簡單。 簡單的結構怎么樣?

struct stuff {
    int id;
    float value;
    char text[8];
};

void assign_thing(stuff *p) {
    *p = {42, 1.5, "Hello!"};
}
movabsq $4593671619917905962, %rax
movq    %rax, (%rdi)
movabsq $36762444129608, %rax
movq    %rax, 8(%rdi)
retq

乍一看有點難以閱讀,但幾乎是相同的想法。 編譯器很聰明,將 integer 和浮點值421.5打包成一個 64 位值,然后直接填充到(%rdi)中。 字符串"Hello!"也是如此。 ,它足夠短,可以放入單個 64 位值並填充到8(%rdi)中( p之后的 8 個字節是text的偏移量)。


到目前為止,memory 在分配時實際上不存在任何右值。 它們只是說明的一部分。 如果它是更大的東西,比如一根繩子怎么辦?

// Overflow checking omitted for brevity.
void assign_thing(char *p) {
    // Assignment with = doesn't actually do what you'd want here,
    // so this'll have to do.
    strcpy(p, "What if it's something a lot bigger, like a string?");
}
vmovups -5484(%rip), %ymm0
vmovups %ymm0, 20(%rdi) ; I'm guessing the disassembler meant to say 0x20
vmovups -5517(%rip), %ymm0
vmovups %ymm0, (%rdi)
vzeroupper
retq

現在,右值在分配時確實駐留在 memory 中。 請注意,這不是因為使用strcpy而不是= ,而是因為編譯器認為最好將該“右值”字符串存儲在.rodata之類的只讀區域中的某個位置,然后將其復制過來。 如果我使用了一個更短的字符串,那么任何合理的現代編譯器都可能會將其優化為一些movmovabsq指令,就像第二個示例中一樣。 除非p指向堆棧上的緩沖區並且您的strcpy最終溢出它,否則這里不會出現堆棧溢出。


現在你的例子呢? 我猜你的string類型真的是std::string ,這不是一個微不足道的類型。 那么那里會發生什么? 在 C++ 中,賦值運算operator= =是可重載的,而std::string確實有自己的重載,所以不是直接將值填充或復制到 object 中,而是調用了一個特殊的成員 ZC1C425268E68385D1AB5074C17A4。 也就是說,你的*str = "some data in a string"實際上是一個str->operator=("some data in a string") 您的右值字符串如何被復制取決於std::string::operator=的實現,但它很可能會被優化為類似於我上一個示例的內容。 std::string的實際字符串數據駐留在堆上,因此堆棧溢出在這里仍然不是問題。


tl;博士(這個答案+評論,壓縮成幾句話)

如果您的字符串足夠小,則在分配期間它可能不會存在於 memory 中。 如果它足夠大,它將位於某處的只讀區域中,並在需要時被復制。 堆棧通常甚至不涉及,因此不必擔心溢出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM