矩阵类的写入大小4无效(使用valgrind)

Invalid write size of 4 with matrix class (using valgrind)

本文关键字:无效 使用 valgrind      更新时间:2023-10-16

我有一个简单的矩阵类,用于opengl(es 2.0,afaik没有任何特定版本的opengl的内置版本(。

它基本上只不过是一个向量,它的大小被调整为在构造函数中包含16个浮点值(我为它的=运算符选择了一个向量而不是16个浮点数组(,以及一些在opengl上下文中有用的便利函数。总之,这些便利结构之一将单位矩阵加载到向量中,它是:

void Matrix::loadIdentity()
{
    matrix.resize(16, 0);
    for(int i = 0; i < 16; i++)
        matrix[i] = 0.0f;
    for(int i = 0; i < 4; i++) {
        matrix[i * 4 + i] = 1.0f;
    }
}

矩阵所在位置:

std::vector<float>

上面写着:

matrix.resize(16, 0);

是为了确保大小实际上是16个浮点,尽管这也是在构造函数中完成的,我认为最终不需要它,但现在要删除一个可能的错误,让调试变得更容易。

Valgrind告诉我这个功能,特别是这行:

matrix[i] = 0.0f;

导致写入大小为4的无效,这听起来很可疑,好像我在以某种方式调用matrix[16]=0.0f;。然而,我应该只从0到15。不过,需要注意的一件有趣的事情是,valgrind还说,被写入的内存是由矩阵的析构函数分配的,更具体地说,是由向量的析构因子分配的。

Address 0x150f8280 is 0 bytes inside a block of size 64 free'd  1: operator delete(void*) in /build/buildd/valgrind-3.6.1/coregrind/m_replacemalloc/vg_replace_malloc.c:387
  2: __gnu_cxx::new_allocator&lt;float&gt;::deallocate(float*, unsigned long) in <a href="file:///usr/include/c++/4.5/ext/new_allocator.h:95" >/usr/include/c++/4.5/ext/new_allocator.h:95</a>
  3: std::_Vector_base&lt;float, std::allocator&lt;float&gt; &gt;::_M_deallocate(float*, unsigned long) in <a href="file:///usr/include/c++/4.5/bits/stl_vector.h:146" >/usr/include/c++/4.5/bits/stl_vector.h:146</a>
  4: std::_Vector_base&lt;float, std::allocator&lt;float&gt; &gt;::~_Vector_base() in <a href="file:///usr/include/c++/4.5/bits/stl_vector.h:132" >/usr/include/c++/4.5/bits/stl_vector.h:132</a>
  5: std::vector&lt;float, std::allocator&lt;float&gt; &gt;::~vector() in <a href="file:///usr/include/c++/4.5/bits/stl_vector.h:314" >/usr/include/c++/4.5/bits/stl_vector.h:314</a>
  6: Matrix::~Matrix() in <a href="file:///home/leif/MarbleMachine/core/matrix.h:6" >core/matrix.h:6</a>

如果这只是某种索引超出范围的错误,那么这似乎不太可能,因为,afaik,向量对象主要是指向数组在内存中的位置的指针(好吧,还有其他东西,但我想指出的是,向量对象本身(析构函数的存储位置(在内存中似乎不可能与实际数组的存储位置连续。此外,最重要的是,当析构函数的全部目的是释放对象使用的内存时,为什么析构函数会声明内存,这难道不会造成内存泄漏吗?顺便说一句,矩阵类没有显式的析构函数,所以只使用隐式析构函数。

最后,我知道valgrind中以前的错误可能会导致未来的错误,但以前唯一的错误是无效的读取大小和在现有库(OpenGL和SDL(中使用未初始化的变量,因此,我发现在这种情况发生之前,它们极不可能导致堆损坏。

谢谢。

从您发布的Valgrind报告片段中可以清楚地看出,您正在访问向量的悬挂存储。

如果一个线程在向量上迭代,而另一个线程调整了向量的大小,那么这种访问很容易出现。例如,这是不安全的:

for (vector<float>::iterator it = matrix.begin(); it != matrix.end(); ++it)
  *it = 0;

而在另一个线程中:

matrix.resize(32, 0);

如果你有可以调整向量大小的突变子,那么你必须序列化所有与它们相关的读取器,例如

pthread_mutex_lock(&matrix_lock);
for (vector<float>::iterator it = matrix.begin(); it != matrix.end(); ++it)
  *it = 0;
pthread_mutex_unlock(&matrix_lock);

在另一个线程中:

pthread_mutex_lock(&matrix_lock);
matrix.resize(32, 0);
pthread_mutex_unlock(&matrix_lock);

你在回答中描述的内容:

1图形线程将matrix.end()置于锁定之外[这已经是一个问题]

6图形线程锁
7图形线程获得matrix.begin()并开始迭代

似乎与Valgrind错误报告不一致,因为这会导致访问超过数组的末尾,而Valgrind报告访问位于(现已删除/悬挂数组(的开始

一句话:调用begin()end()和迭代都必须发生在保护容器的锁内;否则,您的程序行为将是未定义的。

谢谢你的帮助,我想我终于想通了。事实证明,我没有给你足够的信息来解决这个问题,所以在我给你我找到的解决方案之前,让我给你更多的信息。

该应用程序是一个多线程应用程序,在每个线程中,它都有一个部分在一个对象循环(相同的对象(上迭代,其中包含矩阵。在一种情况下,它是一个物理引擎,另一种情况是渲染对象。我确实在适当的时候锁定了两个线程中的数据,但事实证明,我在锁定之前就得到了迭代器的末尾。

所以我认为正在发生的事情是:

  1. 图形线程确定了顶点数组的结束迭代器
  2. 物理线程锁定了数据
  3. 物理线程通过了,由于冲突,向线程添加了新项目
  4. 新数组的大小将大于它的容量,因此它的容量将加倍,从而移动所有数据,并使所有迭代器无效。(我知道每当添加数据时,它们总是无效的,但除非数据的位置发生变化,否则没有不良副作用,afaik(
  5. 物理线程解锁数据
  6. 图形线程锁定数据
  7. 图形线程找到了具有正确位置的起始迭代器,但由于for循环将其与项目#1中的迭代器进行了比较,它偏离了数组的末尾,翻转了无效数据(与设置项目的矩阵以进行绘图有关(

谢谢大家的帮助。