簡體   English   中英

MSVC C++ 編譯器在什么情況下有時會在 function 運算符 new[] 返回的指針之前直接寫入數組大小?

[英]Under what conditions does MSVC C++ Compiler sometimes write the array size directly before the pointer returned from function operator new[]?

我目前正在使用 memory 跟蹤器進行工作,我們正在重載 function 運算符 new[],它有很多變體。 在編寫一些單元測試時,我偶然發現 MSVC C++ 2019(使用 ISO C++ 17 Standard(std:c++17) 編譯器設置),在指針返回到來電者,但只是有時。 我一直找不到任何記錄在案的情況下會發生這種情況。 誰能解釋一下這些條件是什么,我如何在運行時檢測到它們,或者指向我的任何文檔?

為了確定這是否發生,我不得不反匯編代碼。 這是 C++:

const size_t k_NumFoos = 6;
Foo* pFoo = new Foo[k_NumFoos];

這是反匯編:

00007FF747BB3683  call        operator new[] (07FF747A00946h)  
00007FF747BB3688  mov         qword ptr [rbp+19E8h],rax  
00007FF747BB368F  cmp         qword ptr [rbp+19E8h],0  
00007FF747BB3697  je          ____C_A_T_C_H____T_E_S_T____0+0FF7h (07FF747BB36F7h)  
00007FF747BB3699  mov         rax,qword ptr [rbp+19E8h]  
00007FF747BB36A0  mov         qword ptr [rax],6  
00007FF747BB36A7  mov         rax,qword ptr [rbp+19E8h]  
00007FF747BB36AE  add         rax,8  
00007FF747BB36B2  mov         qword ptr [rbp+1B58h],rax  

cmpje行來自我們用於單元測試的 Catch2 庫。 je之后的兩個mov是它寫入數組大小的地方。 接下來的三行( movaddmov )是將指針移動到寫入數組大小之后的位置。 大多數情況下,這一切都很好。

我們還使用 MS 的VirtualAlloc作為重載 function 運算符 new[] 的內部分配器。 The address returned from VirtualAlloc must be aligned for the function operator new[] that uses std::align_t , and when the alignment is greater than the default max alignment, the moving of the pointer in those last three lines of disassembly are messing with the返回對齊的地址。 最初,我認為使用 function 運算符 new[] 進行的所有分配都會有這種行為。 因此,我測試了 function 運算符 new[] 的其他一些用途,發現在我測試的所有情況下都是正確的。 我編寫了代碼來調整這種行為,然后遇到了一種情況,它沒有表現出在返回分配之前寫入數組大小的行為。

這是 C++ 在返回分配之前沒有寫入數組大小的地方:

char **utf8Argv = new char *[ argc ];

argc等於 1。該行來自 Catch2 庫中的Session::applyCommandLine方法。 反匯編看起來像這樣:

00007FF73E189C6A  call        operator new[] (07FF73E07D6D8h)  
00007FF73E189C6F  mov         qword ptr [rbp+168h],rax  
00007FF73E189C76  mov         rax,qword ptr [rbp+168h]  
00007FF73E189C7D  mov         qword ptr [utf8Argv],rax  

注意在call operator new[] (07FF73E07D6F8h)之后沒有寫入數組大小。 在查看兩者的差異時,我可以看到一個寫入指針,而另一個寫入指向指針的指針。 但是,據我所知,在運行時,這些信息在 function 操作員 new[] 內部都不可用。

這里的代碼來自一個 Debug | x64 構建。 關於如何確定這種行為何時發生的任何想法?

更新(下面的convo):Class Foo:

template<size_t ArrLen>
class TFoo
{
public:
    TFoo()
    {
        memset(m_bar, 0, ArrLen);
    }
    TFoo(const TFoo<ArrLen>& other)
    {
        strncpy_s(m_bar, other.m_bar, ArrLen);
    }
    TFoo(TFoo<ArrLen>&& victim)
    {
        strncpy_s(m_bar, victim.m_bar, ArrLen);
    }
    ~TFoo()
    {
    }
    TFoo<ArrLen>& operator= (const TFoo<ArrLen>& other)
    {
        strncpy_s(m_bar, other.m_bar, ArrLen);
    }
    TFoo<ArrLen>& operator= (TFoo<ArrLen>&& victim)
    {
        strncpy_s(m_bar, victim.m_bar, ArrLen);
    }

    const char* GetBar()
    {
        return m_bar;
    }
    void SetBar(const char bar[ArrLen])
    {
        strncpy_s(m_bar, bar, ArrLen);
    }

protected:
    char m_bar[ArrLen];
};
using Foo = TFoo<8>;

猜測一下,我認為編譯器在分配具有析構函數的對象時,會在指針返回給您之前寫出分配的對象數,當您調用delete []時需要調用該析構函數。 在這種情況下,編譯器必須發出代碼來銷毀調用delete []時分配的每個對象,為此,它需要知道數組中存在多少對象。

OTOH,對於char *之類的東西,不需要計數,因此,作為次要優化,不會發出任何內容,或者看起來如此。

我認為您不會在任何地方找到此文檔,並且該行為可能會在編譯器的未來版本中發生變化。 它似乎不是標准的一部分。

暫無
暫無

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

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