为什么我的指针赋值会导致看似不相关的值发生变化?

问题描述 投票:0回答:2

我正在尝试使用双指针解决区间问题。基本前提是在我的映射中存储指向 Interval 结构的双指针,以便我可以更新间隔,而无需迭代映射中的任何其他值。使用双指针似乎会对我的间隔造成一些意外的变化,我不明白为什么。

这是我当前的代码:

#include <iostream>
#include <vector>
#include <utility>
#include <unordered_map>
using namespace std;

int main()
{
    vector<pair<int, char>> sequence = { 
        {0,'a'},
        {1,'-'},
        {2,'b'},
        {3,'c'},
        {4,'-'},
        {5,'d'},
        {6,'e'},
        {7,'-'},
        {8,'f'},
        {10,'g'},
        {11,'-'},
        {12, '-'}
    };

    struct Interval {
        string word;
        bool startH;
        bool endH;
        Interval(string word) : word(word), startH(false), endH(false) {}

    };

    Interval* hyphen = new Interval("-");
    Interval** hPtr = &hyphen;

    auto consolidate = [&hPtr](Interval**& left, Interval**& right) {
        //cout << "\t(" << (*left)->word << " " << (*right)->word << ")" << endl;
        if (left == hPtr && right == hPtr) return;

        if (left == hPtr) {
            (*right)->startH = true;
        }
        else if (right == hPtr) {
            (*left)->endH = true;
        }
        else {
            (*right)->startH = (*left)->startH;
            (*right)->word = (*left)->word + (*right)->word;
            (*left) = (*right);
        }

        if ((*right)->startH && (*right)->endH) {
            cout << "RESULT: " << (*right)->word << endl;
        }
        else if ((*left)->startH && (*left)->endH) {
            cout << "RESULT: " << (*left)->word << endl;
        }
    };


    unordered_map<int, Interval**> intervals;

    for (auto pair : sequence) {
        int index = pair.first;
        char c = pair.second;
        cout << "----" << index << ", " << c << "-----" << endl;
        
        int preIndex = index - 1;
        int postIndex = index + 1;
        auto it_pre = intervals.find(preIndex);
        auto it_post = intervals.find(postIndex);
        
        //HERE 1
        //if (it_pre != intervals.end()) cout << (*(it_pre->second))->word << endl;
        
        Interval** currentIntervalPtr;

        if (c == '-') {
            currentIntervalPtr = hPtr;
        }
        else {
            Interval* currentInterval = new Interval(string(1, c));
            currentIntervalPtr = &currentInterval;
        }

        //HERE 2
        //if (it_pre != intervals.end()) cout << (*(it_pre->second))->word << endl;

        if (it_pre != intervals.end()) {
            Interval** prePtr = it_pre->second;
            //cout << "PRE: <" << (*currentIntervalPtr)->word << ", " << (*prePtr)->word << ">>" << endl;
            consolidate(prePtr, currentIntervalPtr);
        }

        if (it_post != intervals.end()) {
            Interval** postPtr = it_post->second;
            //cout << "POST: <" << (*currentIntervalPtr)->word << ", " << (*postPtr)->word << ">>" << endl;
            consolidate(currentIntervalPtr, postPtr);
        }
        
        intervals[index] = currentIntervalPtr;
    }

    return 0;
}

在我的两个打印语句(HERE 1 和 HERE 2)之间, (*(it_pre->second))->word 的值发生了变化。我在这些语句之间所做的只是创建一个新的、不相关的间隔,所以我不明白为什么我的地图值会发生变化。

任何见解将不胜感激。我是使用双指针的新手,所以我可能只是忽略了一些简单的东西。

c++ pointers
2个回答
0
投票

代码中的主要问题在于以下几行:

Interval* currentInterval = new Interval(string(1, c));
currentIntervalPtr = &currentInterval;

当您执行 ¤tInterval 时,您将获得 currentInterval 指针变量(它是循环中的局部变量)的地址。一旦循环迭代完成, currentInterval 变量就会超出范围并变得无效,但地址仍保留在间隔映射中,指向一些垃圾内存。

因此,在后续的循环迭代中,该内存可能会被重用,从而导致意外的行为。

要解决此问题,您可以将映射更改为存储指针而不是双指针。如果要更新间隔,可以直接通过存储的指针来完成。

这是代码的调整版本:

unordered_map<int, Interval*> intervals;

在循环中,当您创建新间隔时,只需将其直接分配给地图即可:

if (c == '-') {
    currentIntervalPtr = hyphen;
} else {
    currentIntervalPtr = new Interval(string(1, c));
}

当从地图上查找区间时:

if (it_pre != intervals.end()) {
    Interval* preInterval = it_pre->second;
    consolidate(preInterval, currentIntervalPtr);
}

if (it_post != intervals.end()) {
    Interval* postInterval = it_post->second;
    consolidate(currentIntervalPtr, postInterval);
}

您还需要调整合并函数以使用单个指针:

auto consolidate = [&hyphen](Interval*& left, Interval*& right) {
    // ... same as before
};

通过这些更改,您的间隔映射将正确存储和访问间隔,而无需双指针。代码会更简单,行为也将符合预期。


0
投票

这里:

        else {
            Interval* currentInterval = new Interval(string(1, c));
            currentIntervalPtr = &currentInterval;
        }

...您正在将该块的本地变量的地址分配给

currentIntervalPtr
。当块的执行终止时,该指针变量的生命周期结束,之后取消引用
currentIntervalPtr
会产生未定义的行为。

特别注意,指针变量的生命周期与其值所指向的已分配

Interval
对象的生命周期完全分开且独立。

我不太跟踪你想通过这里的双指针获得什么,但也许你可以将

Interval
指针存储在循环外部声明的向量中,而不是指向块范围变量的指针,并且使用指向该向量元素的指针。但我也认为,您可以使用
vector<Interval *>
和单指针来代替
vector<Interval>
和双指针。

© www.soinside.com 2019 - 2024. All rights reserved.