大小 4 的读取无效?

Invalid Read of Size 4?

我在一个程序中 运行 valgrind,虽然程序执行得很好,但 valgrind 报告:

==6542== Invalid read of size 4
==6542==    at 0x8049C6F: Table::removeWebsite(Table&) (Table.cpp:146)
==6542==    by 0x8049768: main (app.cpp:140)
==6542==  Address 0x43f87d4 is 4 bytes inside a block of size 8 free'd
==6542==    at 0x402B528: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-x86-linux.so)
==6542==    by 0x8049BB0: Table::removeWebsite(Table&) (Table.cpp:124)
==6542==    by 0x8049768: main (app.cpp:140)

我不完全确定哪里出了问题。这是 valgrind 指向的代码...

bool Table::removeWebsite(Table& parm)
{
    bool flag = false; //flag to show if an item is removed or not
    int OriTableSize = currCapacity;

    for (int index = 0; index < OriTableSize; index++) //search through array list, starting at index
    {
        Node *current = parm.aTable[index]; 
        Node *prev = nullptr;
        while (current != nullptr) //search through linked lists at array[index]
        {
            if (current->data->getRating() == 1) //search ratings for 1
            {
                if (prev == nullptr) //if current is the start of the list
                {
                    if (current->next == nullptr) //if current is the only item in this list
                    {
                        delete current;
                        size--;
                        parm.aTable[index] = nullptr;
                        flag = true;
                    }
                    else
                    {
                        parm.aTable[index] = current->next; //point to the next item in list
                        delete current;
                        size--;
                        flag = true;
                    }
                }
                else //reset prev->next pointer to skip current
                {
                    prev->next = current->next;
                    delete current;
                    size--;
                    flag = true;
                }
            }
            prev = current;
            current = current->next; //go to next position in linked list
        }
    }

    if (flag == true)//at least one item was removed
        return true;
    else
        return false;
}

那个"delete current"指向一个节点,它有:

        struct Node
        {
                Node(const SavedWebsites& parm)
                {
                        data = new SavedWebsites(parm);
                        next = nullptr;
                };
                ~Node()
                {
                    delete data;
                    next = nullptr;
                }

                SavedWebsites *data;
                Node *next;
        };

并且 SavedWebsites 中的数据具有析构函数:

//Default Destructor
SavedWebsites::~SavedWebsites()
{
    if (this->topic)
    {
        delete [] this->topic;
        this->topic = nullptr;
    }
    if (this->website)
    {
        delete [] this->website;
        this->website = nullptr;
    }
    if (this->summary)
    {
        delete [] this->summary;
        this->summary = nullptr;
    }
    if (this->review)
    {
        delete [] this->review;
        this->review = nullptr;
    }
    rating = 0;
}

*注意项目 "topic" "website" "summary" 和 "review" 都是用

创建的
... = new char[strlen(topic)+1] //(We have to use Cstrings)

我对这一切还很陌生,所以对可能导致此 valgrind 无效读取的原因有什么想法吗?

您没有指出哪一行对应于 Table.cpp:146,但根据 Valgrind 的输出,看起来有问题的分配代表一个 Node 实例,因为 8 个字节的大小匹配(假设32 位系统)。无效读取是由该实例的偏移量 4 和大小 4 触发的,因此这必须对应于成员 next.

Table.cpp:146:
            current = current->next; //go to next position in linked list

如果在迭代中出现 delete,则之后访问已删除的节点 current。您还根据无效指针初始化 prev,因此在删除的情况下,最后一个有效节点的 next 指针将不会正确更新。

您的代码似乎工作正常(但不是),因为它的行为取决于使用的分配器和其他分配。如果分配器要用某种模式填充释放的内存,或者如果该内存被立即重用并写入,您会看到分段错误。

为了解决这个问题,我会推迟 delete。在下一次迭代之前,您可以读取下一个节点并删除原始 current 或更新 prev 并读取下一个节点。

            bool del = false;
            if (current->data->getRating() == 1) //search ratings for 1
            {
                if (prev == nullptr) //if current is the start of the list
                {
                    if (current->next == nullptr) //if current is the only item in this list
                    {
                        parm.aTable[index] = nullptr;
                    }
                    else
                    {
                        parm.aTable[index] = current->next; //point to the next item in list
                    }
                    del = true;
                    flag = true;
                }
                else //reset prev->next pointer to skip current
                {
                    prev->next = current->next;
                    del = true
                    flag = true;
                }
            }
            if (del)
            {
                Node *deletenode = current;
                current = current->next;
                delete deletenode;
                size--;
            }
            else
            {
                prev = current;
                current = current->next; //go to next position in linked list
            }