[英]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. cmp
和je
行来自我们用于单元测试的 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.接下来的三行(
mov
、 add
、 mov
)是将指针移动到写入数组大小之后的位置。 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.