【问题标题】:Dynamic Memory Allocation for an array given a specific memory address to start with给定特定内存地址的数组的动态内存分配开始
【发布时间】:2023-04-03 20:30:01
【问题描述】:

正如标题所暗示的那样,当您给出地址时,是否可以开始一个数组。即如果我的起始地址是10002432,则基本上&array[0] = 10002432array[1] 将存储在下一个顺序地址中。我可以使用指针复制到所述地址,但我想使用动态分配的数组而不是指针。我当前的代码如下使用指针。我尝试了目前在线的各种解决方案,但没有一个能够提供明确的解决方案。

我正在考虑创建一个重载的 new 运算符,但我也卡住了,因为在重载的 new 运算符的基础上我们使用 malloc,它也不允许指定起始地址。

我在模拟器 (gem5) 中运行此代码,在那里我可以控制内存地址,因为我可以指定虚拟地址如何映射到物理地址。

int main(int argc, char const *argv[])
{

  uintptr_t address = 10002432;
  int *Pointer = (int*) address;
  int *Pointer2 = Pointer; 

  for (int i = 0; i < 32; ++i)
    {    
        *Pointer = i;
        Pointer = Pointer + 4;
    }


    for (int i = 0; i < 32; ++i)
    {    
        printf("%d\n",Pointer2 );
        Pointer2 = Pointer2 + 4;
    }

}

我们将不胜感激。

【问题讨论】:

  • Google 的短语是“placement new”,它允许您在内存中的指定位置构造对象。
  • 在问题中添加您想要这个的原因可能会有所帮助。建议:轻松开始。确保您的代码将在正常分配给程序的缓冲区中正常工作。在尝试使用您的进程甚至可能不拥有的内存之前,先消除简单的错误。
  • 顺便说一句,这段代码是错误的:Pointer = Pointer + 4.
  • 原地分配数组的原因是什么?我猜你遇到了问题 XY 偏差。
  • @TortelliniTuesday,确切地说,它会跳转4*sizeof(int),我猜这不是作者的意思。

标签: c++ arrays memory-management


【解决方案1】:

不,不可能从标准 C++ 中的特定地址“分配”内存。

在使用虚拟内存的操作系统(即所有现代多任务操作系统)中,通常也没有特定于系统的方法来进行此类分配。然而,在嵌入式系统上使用系统提供商记录的常量地址是很常见的。您通常可以考虑“始终分配”此类内存。也许这适用于您的模拟器。

您可以通过指针将动态对象创建到未初始化的内存中。执行此操作的语法称为placement new。这有时对于为某些就地容器(例如std::vector)重用“通用”分配的内存很有用,以及在地址不变的情况下。

重要提示:假定地址满足创建对象的对齐要求。如果不满足,则程序的行为未定义。

虽然有这样做的语法,但实际上不可能以这种方式创建数组。数组放置新不能保证从提供的地址开始数组。从地址开始的一些实现特定的内存量可能会被语言实现使用。

您可以简单地单独、单独地创建元素,并假装它们在一个数组中,而不是创建一个数组。这就是您的标准库中std::vector 的实现可能也是如此。标准库中有相应的辅助函数:

using T = int;
std::uintptr_t address = 10002432; // Note: using hex would be more conventional
const std::size_t size = 32;

T* p = std::launder(reinterpret_cast<T*>(address));
if (!std::align(alignof(T), sizeof(T), p, sizeof(T)))
    throw std::invalid_argument("bad alignment");

std::uninitialized_fill_n(p, size, T{});
// use it
std::destroy_n(p, size);

// Instead of filling from the value initialised value,
// you can copy a range. Here is an example with C++20 ranges:

auto i = std::views::iota(T{});
std::uninitialized_copy_n(i.begin(), size, p);
// use it
std::destroy_n(p, size);

请注意,在int 和其他普通类型的情况下,销毁部分是不必要的,但对于非普通可破坏类型是必需的。这是一个简单的例子,并不是异常安全的。如果初始化成功后抛出异常,则不会调用销毁。您可以使用 RAII 习语来确保发生破坏。

如果给定的指针派生自指向先前存在的对象的有效指针,例如在重用普通对象的内存时(示例中的情况不是),则std::launder 是必要的如示例中使用的那样。否则可能没有必要(示例中就是这种情况,因此如果您知道自己在做什么,就可以摆脱洗钱;但保持它并没有什么坏处)。

当将整数重新解释为指针时,语言标准不保证该值将映射到哪个内存地址。您必须依靠语言实现提供的保证,才能使该内存地址以任何方式有用。除非您从有效指针转换为整数(足够大小),然后再转换回相同的指针类型,否则保证是相同的指针值。但是这种情况在例子中并不适用。

【讨论】:

  • 我觉得launder 在这里可能不太合适,但我不是很熟悉。
  • @MooingDuck 我确实解释过std::launder 在这种特殊情况下可能不需要。但总的来说,在某些涉及放置 new 的情况下可能需要它(事实上,没有其他需要清洗的情况,据我所知,除非为新对象重新使用内存),所以我把它放在这里案例有人将代码用于稍微不同的用例。这是(在我看来)很难弄清楚什么时候需要它的事情之一,但使用它总是不会伤害任何东西,所以为什么不呢。
猜你喜欢
  • 1970-01-01
  • 2012-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-31
相关资源
最近更新 更多