sha*_*oth 11 c++ memory memory-management
受此问题的启发,关于编译器是否可以优化掉对函数的调用而没有副作用.假设我有以下代码:
delete[] new char[10];
Run Code Online (Sandbox Code Playgroud)
它没有任何用处.但它有副作用吗?堆分配后立即释放被认为是副作用吗?
Ste*_*sop 10
这取决于实施.除非实现决定它是可观察的行为,否则分配和释放内存不是"可观察的行为".
在实践中,您的实现可能链接到某种类型的C++运行时库,并且当您的TU被编译时,编译器被迫识别对该库的调用可能具有可观察的影响.据我所知,这不是标准规定的,它只是通常的工作方式.如果优化器可以某种方式确定某些调用或调用组合实际上不会影响可观察行为,那么它可以删除它们,所以我相信一个特殊情况来发现你的示例代码并删除它会符合.
另外,我不记得用户定义的全局 [我已被提醒].由于代码可能会在另一个用户定义的TU中调用这些内容的定义,这些TU稍后会链接到此TU,因此无法在编译时优化调用.如果结果表明操作符不是用户定义的(尽管那时候运行库的内容适用),或者是用户定义的但没有副作用(一旦它们一对),它们可以在链接时删除内联 - 这在合理的实现中似乎非常难以置信,实际上[*]).new[]和delete[]工作方式
我很确定你不会依赖异常new[]来"证明"你是否已经耗尽内存.换句话说,只是因为new char[10]没有抛出这个时间,并不意味着它在你释放内存并再试一次之后就不会抛出.而且仅仅因为它最后一次投掷而你从未放过任何东西,并不意味着它会抛出这个时间.因此,我认为没有任何理由可以解释为什么这两个调用无法消除 - 没有标准保证new char[10]会抛出的情况,所以不需要实现来确定它是否会.众所周知,系统上的其他进程在调用之前释放了10个字节new[],并在调用之后立即分配delete[].
[*]
或者可能不是.如果new不检查空间,可能依赖于保护页面,但只是递增一个指针,并且delete通常什么也不做(依赖于进程退出以释放内存),但是在释放的块是分配的最后一个块的特殊情况下,它会减少指针,你的代码可能相当于:
// new[]
global_last_allocation = global_next_allocation;
global_next_allocation += 10 + sizeof(size_t);
char *tmp = global_last_allocation;
*((size_t *)tmp) = 10; // code to handle alignment requirements is omitted
tmp += sizeof(size_t);
// delete[]
tmp -= sizeof(size_t);
if (tmp == global_last_allocation) {
global_next_allocation -= 10 + *((size_t*)tmp);
}
Run Code Online (Sandbox Code Playgroud)
假设没有任何东西是挥发性的,那么几乎所有都可以被删除global_last_allocation = global_next_allocation;.您也可以通过将last块头中的先前值与大小一起存储,并在释放最后一个分配时恢复该先前值来消除这种情况.这是一个非常极端的内存分配器实现,但是,你需要一个单线程程序,一个速度恶魔程序员,他相信程序不会通过比开始时可用的内存更多的内存.
| 归档时间: |
|
| 查看次数: |
396 次 |
| 最近记录: |