【问题标题】:Strange behaviour with std::stack, pop() returns same valuestd::stack 的奇怪行为,pop() 返回相同的值
【发布时间】:2018-07-30 06:04:00
【问题描述】:

我的课程使用std::stack:

class NotificationService{
    public:
        void addPendingNotification(uint8_t *uuid);
        uint8_t* getNextPendingNotification();
        void popPending();
    private:
        std::stack<uint8_t*> pendingNotification;
};

void NotificationService::addPendingNotification(uint8_t *uuid) {
    pendingNotification.push(uuid);
    Serial.print("Insert to stack: ");
    Serial.print(uuid[0]);
    Serial.print(uuid[1]);
    Serial.print(uuid[2]);
    Serial.println(uuid[3]);
}

uint8_t *NotificationService::getNextPendingNotification() {
    if (pendingNotification.size() > 0) {
        uint8_t *uuid = pendingNotification.top();
        Serial.println(*uuid);
        pendingNotification.pop();
        return uuid;
    } else {
        return NULL;
    }
};

void NotificationService::popPending(){
    while (!pendingNotification.empty())
    {
        uint8_t *uuid = pendingNotification.top();
        Serial.print(uuid[0]);
        Serial.print(uuid[1]);
        Serial.print(uuid[2]);
        Serial.println(uuid[3]);
        pendingNotification.pop();
    }
}

我在我的主代码中添加到堆栈(BLE 通知回调):

static void NotificationSourceNotifyCallback(
    BLERemoteCharacteristic *pNotificationSourceCharacteristic,
    uint8_t *pData,
    size_t length,
    bool isNotify)
{
    if (pData[0] == 0)
    {
        uint8_t messageId[4] = {pData[4], pData[5], pData[6], pData[7]};
        switch (pData[2])
        {
            //Incoming Call
        case 1:
        {
            notificationService->addPendingNotification(messageId);
        }
/** code **/
}

一切正常,直到我想从堆栈中弹出项目,然后每个项目都具有相同的值(最后插入的元素)。

串行打印日志:

Insert to stack: 8000
Insert to stack: 32000
Insert to stack: 19000
Insert to stack: 44000
Insert to stack: 4000
Pop whole stack:
4000
4000
4000
4000
4000

所以我尝试在在线编译器中编写类似的代码:

http://cpp.sh/7hv4

而且效果很好。

我做错了什么?

【问题讨论】:

  • 我的 猜测(因为您没有显示Minimal, Complete, and Verifiable Example)是您将指针传递给单个变量,因此所有指针在堆栈中指向该单个变量。我建议您为“uuid”创建一种不同的类型,一种可以复制或移动并按值传递的类型。或者也许重用像std::array&lt;uint8_t, 4&gt;这样的类型?
  • 检查从每个top 调用返回的地址应该会让您想知道这是怎么可能的。倒计时,看看你通过每个调用推送到notificationService-&gt;addPendingNotification(messageId); 的内容将确认这一点。更糟糕的是,无论如何,您都在调用未定义的行为。您有效地将 dangling 指针推入堆栈。每次退出if (pData[0] == 0) 作用域块时,刚刚压入堆栈的messageId 表示的地址不再对解引用有效。而且你有一整堆这样的地址。
  • 什么是strack?

标签: c++ arduino stack esp32


【解决方案1】:
std::stack<uint8_t*> pendingNotification;

你有一堆指针。为此,您必须有一堆不同的对象供堆栈保存指针,并且只要您打算使用指针堆栈,这些对象就必须保持有效。您的代码没有这样做。

除非您有充分的理由,否则不要创建指针堆栈。相反,创建数据值堆栈。

【讨论】:

    【解决方案2】:
    notificationService->addPendingNotification(messageId);
    

    将指向局部变量(messageId 数组)的指针压入堆栈。

    这个局部变量的范围在稍后的某个时间点结束(只要封闭的if 块结束)。但是堆栈中的指针仍然存在。此时,取消引用此指针会调用undefined behavior,因此当您从堆栈中弹出指针后执行此操作时,您得到的结果是未定义的行为。

    在您的具体情况下,编译器似乎为所有 messageId 实例重新使用了相同的内存位置,这意味着在推送所有项目后,该内存位置仍然保存最后推送的项目的值。但要重申:这是未定义的行为,你不应该依赖它。

    相反,要么将的副本压入堆栈,要么将堆栈上的指针压入内存,该内存将在指针(堆栈)的生命周期内保持分配状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-22
      • 1970-01-01
      • 2015-12-28
      • 2011-03-06
      • 2020-06-11
      • 2013-03-05
      相关资源
      最近更新 更多