【问题标题】:Soul-crushing C++ std::vector::resize() Access Violation Error [closed]灵魂破碎的 C++ std::vector::resize() 访问冲突错误 [关闭]
【发布时间】:2011-03-26 05:17:11
【问题描述】:
class SimpleVariant
{
public:
    SimpleVariant()  { /*...*/ };
    // ...
};


struct VariantBlock
{
    int nRows, nCols;
    vector<SimpleVariant> theData;
};



void dumbFunction( VariantBlock& theBlock, int nRows, int nCols )
{
    // ...
    cout << "theBlock.nRows= " << theBlock.nRows 
         << ", theBlock.nCols= " << theBlock.nCols
         << ", theBlock.theData.size() " << theBlock.theData.size();

    theBlock.theData.resize( nRows * nCols );   
       // throws Access Violation Exception

    // ...
} 

在抛出访问冲突异常之前,输出返回 nRows=61、nCols=5、size()=0,这正是此时应有的值。

我用的是MSVC6,这显然不是最优的,但此时别无选择。

【问题讨论】:

  • 这可能是错误的复制行为,正如 James 所指出的,但也可能是程序中其他地方的内存损坏。
  • “程序中其他地方的内存损坏”——让我想到可能是这种情况的一件事是,最初,程序中的其他地方似乎抛出了异常。在放入各种“cout”调试行后,错误似乎移动了。
  • 您能否在 VS2008 或 VS2010 Express 中尝试相同的操作来证明是 VC6 导致了问题?众所周知,VC6 STL 实现非常糟糕。见stackoverflow.com/questions/191253epsilon-delta.net/articles/vc6_stl.html
  • @Doug:您可以从代码中验证自己,但一种实现的文档表明是的:“如果 sz 大于当前向量大小,则通过在末尾插入尽可能多的副本来扩展内容的 c 以达到 sz 元素的大小。这可能会导致重新分配。"
  • 这种bug需要valgrind.org之类的工具

标签: c++ resize access-violation stdvector


【解决方案1】:

我最近不得不升级一些最初为 Visual C++ 6 编写的代码。该代码存在问题,因为 VC++ 6 无法正确处理可以绑定到引用的内容。这有点像在黑暗中拍摄,但是您是否将const VariantBlock 传递给dumbFunction?在 C++ 规则下,这是非法的,但我强烈怀疑 VC++ 6 会出错。

另一种可能性是某种运行时不匹配。如果 (1) VariantBlock 分配在一个模块中,并且 (2) dumbFunction 来自不同的模块,并且 (3) 它们是使用不同的设置编译的,可能是编译器的不同版本,然后你会看到这种行为(resize() 分配新内存,将所有内容复制到它,然后去释放旧内存,除了旧内存是在不同的运行时分配的,所以程序 barfs)。

简而言之,您发布的代码非常好。还有其他事情发生。

【讨论】:

    【解决方案2】:

    我是主播。

    问题是调用函数的内存损坏。

    【讨论】:

      【解决方案3】:

      我认为你在那个错误之前做错了什么。 std::vector::resize 操作将要求内存,而堆很容易成为损坏的受害者。未定义行为的坏处在于,在错误发生一百万条执行指令后,症状就会变得可见(即“任何事情都可能发生”包括“什么都没有”)。

      我们有一个“调试内存管理器”,它重新定义了全局分配器,并且可以对损坏进行大量检查:

      1. 使用不明显的位模式初始化分配的内存(这是为了在有人使用未初始化的内存时发现问题,并且显然代码在找到零时有效)
      2. 在每个内存块之前和之后添加一些“安全区域”,并检查删除是否未被覆盖(这是用于缓冲区溢出检测)
      3. 在释放时使用不同的特定模式填充内存(这是为了尝试捕获 read-after-delete 错误)
      4. 重用内存块并在重新分配或全局验证时检查已删除的内存模式是否仍然存在(这是为了捕获 write-after-delete 错误)
      5. __FILE__/__LINE__ 信息标记每个块,以检测谁在泄漏内存泄漏,并能够判断谁在使用删除后损坏的块。

      我们还有一个内存检查例程,可以根据需要遍历所有内存块并检查一致性(通常我们只检查分配/解除分配)。我们也可以在绝望的情况下注销所有内存分配/释放的完整列表。

      不幸的是,对于第 (5) 点,C++ 语法很难检测,因此我们实际上不使用new,而是使用最终扩展为布局分配的xnew 宏;这也意味着我们无法检测在标准库中分配的内存块(但是,我们存储在库中分配发生之前在我们的程序中进行分配的最后一个源代码行是什么)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-15
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        • 2020-09-22
        相关资源
        最近更新 更多