简体   繁体   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[]?

I'm currently working on a memory tracker for work, and we are overloading the function operator new[], in its many variations.我目前正在使用 memory 跟踪器进行工作,我们正在重载 function 运算符 new[],它有很多变体。 While writing some unit tests, I stumbled across the fact that MSVC C++ 2019 (using the ISO C++ 17 Standard(std:c++17) compiler setting), writes the size of the allocated array of objects directly before the pointer returned to the caller, but only sometimes.在编写一些单元测试时,我偶然发现 MSVC C++ 2019(使用 ISO C++ 17 Standard(std:c++17) 编译器设置),在指针返回到来电者,但只是有时。 I have been unable to find any documented conditions under which this will occur.我一直找不到任何记录在案的情况下会发生这种情况。 Can anyone please explain what those conditions are, how I can detect them at runtime, and or point me to any documentation?谁能解释一下这些条件是什么,我如何在运行时检测到它们,或者指向我的任何文档?

To even determine this was happening, I had to disassemble the code.为了确定这是否发生,我不得不反汇编代码。 Here is the C++:这是 C++:

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

And here is the disassembly:这是反汇编:

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  

The cmp and je lines are from the Catch2 library we are using for our unit tests. cmpje行来自我们用于单元测试的 Catch2 库。 The subsequent two mov s, following the je , are where it's writing the array size. je之后的两个mov是它写入数组大小的地方。 The next three lines ( mov , add , mov ) are where it's moving the pointer to after where it has written the array size.接下来的三行( movaddmov )是将指针移动到写入数组大小之后的位置。 This is all well and good, mostly.大多数情况下,这一切都很好。

We are also using MS's VirtualAlloc as the allocator internal to the overloaded function operator new[].我们还使用 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 aligned address being returned. 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返回对齐的地址。 Initially, I thought all allocations made with function operator new[] would have this behavior.最初,我认为使用 function 运算符 new[] 进行的所有分配都会有这种行为。 So, I tested some other uses of function operator new[], and found it to be true in all cases I tested.因此,我测试了 function 运算符 new[] 的其他一些用途,发现在我测试的所有情况下都是正确的。 I wrote the code to adjust for this behavior, and then ran into a case where it doesn't exhibit the behavior of writing the array size before the returned allocation.我编写了代码来调整这种行为,然后遇到了一种情况,它没有表现出在返回分配之前写入数组大小的行为。

Here is the C++ of where it is not writing the array size before the returned allocation:这是 C++ 在返回分配之前没有写入数组大小的地方:

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

argc is equal to 1. The line comes from the Session::applyCommandLine method in the Catch2 library. argc等于 1。该行来自 Catch2 库中的Session::applyCommandLine方法。 The disassembly looks like so:反汇编看起来像这样:

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  

Notice after the call to operator new[] (07FF73E07D6F8h) there is no writing of the array size.注意在call operator new[] (07FF73E07D6F8h)之后没有写入数组大小。 When looking at the two for differences, I can see that one writes to a pointer, while the other writes to a pointer to a pointer.在查看两者的差异时,我可以看到一个写入指针,而另一个写入指向指针的指针。 However, none of that information is available internally, at runtime, to function operator new[], as far as I know.但是,据我所知,在运行时,这些信息在 function 操作员 new[] 内部都不可用。

The code here comes from a Debug |这里的代码来自一个 Debug | x64 build. x64 构建。 Any ideas on how to determine when this behavior will occur?关于如何确定这种行为何时发生的任何想法?

Update (for convo below): Class Foo:更新(下面的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>;

At a guess, I would think the compiler would write the number of objects allocated out before the pointer returned to you when it is allocating objects which have a destructor that needs to be called when you call delete [] .猜测一下,我认为编译器在分配具有析构函数的对象时,会在指针返回给您之前写出分配的对象数,当您调用delete []时需要调用该析构函数。 Under those circumstances, the compiler has to emit code to destroy each of the objects allocated when you call delete [] , and to do that, it needs to know how many objects are present in the array.在这种情况下,编译器必须发出代码来销毁调用delete []时分配的每个对象,为此,它需要知道数组中存在多少对象。

OTOH, for something like char * , no count is needed, and so, as a minor optimisation, none is emitted, or so it would seem. OTOH,对于char *之类的东西,不需要计数,因此,作为次要优化,不会发出任何内容,或者看起来如此。

I don't suppose you'll find this documented anywhere and the behaviour might change in future versions of the compiler.我认为您不会在任何地方找到此文档,并且该行为可能会在编译器的未来版本中发生变化。 It doesn't seem to be part of the standard.它似乎不是标准的一部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从指向从函数返回的数组的指针中打印值 (C++) - Printing values from a pointer to an array returned from a function (C++) C ++ new运算符是否保证返回的指针不会改变其值? - Does C++ new operator guarantee that the returned pointer will not change its value? 通过指针从c ++中的函数返回数组,但是程序如何知道数组大小? - Return array from function in c++ through pointer, but how does the program know the array size? 在C ++中取消引用该指针之前,递增该函数返回的指针 - Incrementing a Pointer Returned by a Function before Dereferencing that Pointer in C++ 如何在 ZC1C425268E68385D1AB5074C1 return 之前从 C++ DLL 到 Delphi 发送新大小的数组 - How to send new size of array from C++ DLL to Delphi before function return C/C++ 编译器如何区分 * 运算符(指针、取消引用运算符、乘法运算符)的用途? - How does the C/C++ compiler distinguish the uses of the * operator (pointer, dereference operator, multiplication operator)? C ++返回的指针指向的不是函数返回的指针 - C++ returned pointer points to something other than what was returned from the function MSVC C ++编译器限制 - 特性在CRTP下不起作用 - MSVC C++ Compiler Limitation - Trait Not Working Under CRTP C ++将新数组传递给函数的指针 - C++ Passing an new array to a pointer from a function C ++中new运算符的返回类型是什么?为什么我们可以将返回值分配给变量(非指针)? - what is the return type of the new operator in c++ & why can we assign the returned value to a variable(non-pointer)?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM