【问题标题】:C++ Void double pointer interference with anotherC++ void 双指针相互干扰
【发布时间】:2020-04-08 11:02:27
【问题描述】:

我正在尝试做一个涉及重载 new 和 delete 的项目。我将一个 void 指针数组(指向分配的随机内存的指针)存储在一个双 void 指针中。当然,需要调整这个数组的大小以考虑更多的 void 指针。

这是数组的初始化,allocArraySize(int)设置为4。

void** allocArray = (void**) calloc ( allocArraySize, sizeof(void*) );

将所有值设置为 nullptr..

for(int i=0; i<allocArraySize; i++){ *(allocArray + i*sizeof(void*)) = nullptr; }

但是,在尝试调整数组大小时,我注意到当我创建一个新的临时数组(temp)来存储它们时,我原来的 void 指针数组被修改了。

// PRINTS THE ORIGINAL ARRAY 
for(int i=0; i<allocArraySize; i++){std::cout<<i<<" = "<<*( allocArray + i*sizeof(void*))<<"\n";}

void** tempArray = (void**)calloc(allocArraySize, sizeof(void*));

// PRINTS THE ORIGINAL ARRAY 
for(int i=0; i<allocArraySize; i++){std::cout<<i<<" = "<<*( allocArray + i*sizeof(void*))<<"\n";}`

请注意,我什至还没有将数组值放入临时数组中,我仍然遇到这个问题。这是为什么?为什么在确实初始化时将这个随机值分配给这个点?这个新的临时数组变量是怎么弄乱我的数组的?

注意:这基本上是完整的代码。唯一缺少的是主函数、iostream 和 allocArraySize 的声明。是的,我知道我没有释放这些可怜的双指针。我只是想尽可能用最简单的方式来制造问题。

【问题讨论】:

  • 为什么 C++ 代码会使用 calloc...
  • 仅供参考。将所有指针设置为nullptr 的循环毫无意义。在来自calloc 的零填充之后,它们已经为空。并且您在打印期间的sizeof 操作不应该存在。指针算法已经为您处理了偏移量。
  • allocArray + i*sizeof(void*) 将在i 大于 0 时超出数组的末尾,因为 arrayAllocSize 是 4。
  • 请添加一个输入和输入的输出,如果可能的话还要加上预期的输出
  • 这都是技术上未定义的行为,您应该使用容器,例如 vector

标签: c++ memory void-pointers double-pointer


【解决方案1】:

您错误地索引了指针i * sizeof(void*) 而不是i

for(int i=0; i<allocArraySize; i++){ allocArray[i] = nullptr; }

【讨论】:

    【解决方案2】:

    sizeof 乘数不应该存在:

    *(allocArray + i*sizeof(void*))
    //              ^^^^^^^^^^^^^^ this shouldn't be here
    

    void** 是强类型的。它通过指针运算参与适当的偏移量计算。这里不需要 sizeof 偏移量计算。该循环似乎是为了转储序列中的指针值而设计的,因此应该是:

    for(int i=0; i<allocArraySize; i++)
    {
        std::cout<< i << " = " << allocArray[i] << "\n";
    }
    

    【讨论】:

    • 所有指针算术都“参与正确的偏移量计算”,除了格式错误的/UB代码。
    • @M.M 您是否有特定原因删除了该手术引文的“通过指针算术”部分?只是想了解你似乎认为我错过了什么。
    • 您似乎在暗示还有其他情况不执行“正确的偏移计算”或需要 sizeof 偏移计算的地方
    【解决方案3】:

    首先,您不应该在 C++ 代码中使用 calloc。特别是如果你的程序同时使用 new 表达式和 calloc\malloc,可能会因为不匹配的删除器而导致 UB。

    allocArraySize 得到类型 void** ,所以它是指向指针的指针。表达式 *allocArraySize 的结果与 void*uintptr_t 类型的 sizeof 相同。带有指针的算术会根据指向下一个相同类型对象所需的数量自动增加指针。

    C++ 的编写方式甚至不需要这些晦涩难懂的知识,您只需使用新表达式并用空列表对其进行列表初始化即可获得与 C 中 calloc 相同的效果。

    void** allocArray = ::new void*[allocArraySize] {};
    
    for(int i = 0; i < allocArraySize; ++i)
    {
         std::cout<< i << " = " << allocArray[i] << "\n";
    }
    

    在重载的内部使用 new/delete 时,必须使用 :: 否则会导致无限递归。

    allocArray[i] 等效于 *(allocArray + i)

    重载 new\delete 放在一边。但真正做到这一点的 C++ 方法是避免裸指针并尽可能使用容器。 New\delete 可能是一些内存池类使用的包装器。

    // do we really need a vector of pointers?
    auto allocArray = std::vector<void*>(allocArraySize, nullptr);
    
    int i = 0;
    for( auto &v : allocArray )
        std::cout<< (i++) << " = " << v << "\n";
    

    在 C++20 中,由于 init 语句,基于范围的 for 循环变得更加包含

    for(int i = 0; auto &v : allocArray )
          std::cout<< (i++) << " = " << v << "\n";
    

    【讨论】:

    • 如果您重载 new & delete,并且使用 std::vector 来执行此操作,那么您做错了。
    猜你喜欢
    • 2013-05-27
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 2023-01-31
    • 2013-12-18
    • 2013-05-09
    • 2011-07-14
    • 2021-04-08
    相关资源
    最近更新 更多