忘崽 2022-03-27 17:07 采纳率: 100%
浏览 378
已结题

结构体数组访问溢出,在接收实时数据时,ROS会报错段错误 (核心已转储)

“段错误”是对内存非法调用的一个总称,因为从汇编的层面上来看,分配给可执行程序的内存都是分段使用的,有的段存储可执行代码,有的段存储堆,有的段存放静态数据等;

待解决,经排查初步认定是内存的过界访问(结构体数组的访问溢出)

背景:我在用一个UWB测距模块,在Nooploop空循环官方ROS驱动包中,发布标签节点的同时,在订阅方的回调函数中使用一个名为LinktrackAoaNodeframe0.msg的消息文件,内容如下:

img

查阅ROS Wiki相关资料,LinktrackAoaNode0[] nodes是msg文件结构体数组的一种定义方式,而在LinktrackAoaNode0这个msg文件中,可以看到nodes这个结构体数组中的成员,内容如下:

img

现在我仅仅想取LinktrackAoaNode0这个msg文件中的dis(float32)与angle(float32)两个成员,即距离与角度。然后就有以下问题:

img

img

img

img

我以两种方式打印数据,在程序在跑一段时间后会强行中止(不超过10秒),都会出现段错误(核心已转储),上网查阅很可能是“数组、向量等类似的类型是否产生了越界访问”

在去掉含数组的访问操作后,如下图,会发现程序会一直执行,不会报错。

img

img

有人可能会疑问nodes[]里的下标为什么是0?

msg->nodes[0].dis

我们可以简单看一下,发布方其中的一段内容:

  void Init::InitAoaNodeFrame0(NProtocolExtracter *protocol_extraction)
  {
    auto protocol = new NLTAoa_ProtocolNodeFrame0;
    protocol_extraction->AddProtocol(protocol);
    protocol->SetHandleDataCallback([=] {
      if (!publishers_[protocol])
      {
        auto topic = "nlink_linktrack_aoa_nodeframe0";
        publishers_[protocol] =
            nh_.advertise<nlink_parser::LinktrackAoaNodeframe0>(topic, 200);
        TopicAdvertisedTip(topic);
      }
      const auto &data = g_nltaoa_nodeframe0.result;
      auto &msg_data = g_msg_aoa_nodeframe0;
      auto &msg_nodes = msg_data.nodes;
 
      msg_data.role = data.role;
      msg_data.id = data.id;
      msg_data.local_time = data.local_time;
      msg_data.system_time = data.system_time;
      msg_data.voltage = data.voltage;
      
      msg_nodes.resize(data.valid_node_count);
      for (size_t i = 0; i < data.valid_node_count; ++i)
      {
        auto &msg_node = msg_nodes[i];
        auto node = data.nodes[i];
        msg_node.id = node->id;
        msg_node.role = node->role;
        msg_node.dis = node->dis;
        msg_node.angle = node->angle;
        msg_node.fp_rssi = node->fp_rssi;
        msg_node.rx_rssi = node->rx_rssi;
      }
 
      publishers_.at(protocol).publish(msg_data);
    });
  }

我们只需要关注for (size_t i = 0; i < data.valid_node_count; ++i)中的valid_node_count,它代表基站的个数,而我只有一个标签和一个基站,即valid_node_count=1(valid_node_count如果为0,代表当前这一帧数据里面没有标签到基站的数据,可能是基站没有上电,也可能是丢包)

因为数组的第一个元素的下标为0,那么msg->nodes[0].dis代表第一个基站返回到标签的距离信息(因为我只有一个基站下标也只能为零,其它下标是不会返回数据的,而且理论上nodes.size的大小也应该等于1,我们可以验证一下)

img

img

可以看到距离和角度为无效信息。

查阅该AOA模块的用户手册中的协议(说明一下,当标签与基站上电后,协议会自行解析,我们要关注的是怎么使用解析后的数据信息)

img

其中valid_node_quantity变量代表这一帧数据里有几个block(变长协议),每个block里包含一个基站到标签的距离角度等数据。下图是block0一帧数据的所有信息。

img

这就是选nodes[0]的原因,回到一开始的问题,为什么出现“段错误 (核心已转储)”的意外中止,在去掉含数组的访问操作后,程序会一直执行,不会报错。我怀疑是结构体数组数据的访问溢出,那么这就是一个C++的语法问题了。

请问各位码友,那么我也该如何在回调函数中访问结构体数组才不会溢出呢? 还是说是其他问题?

  • 写回答

1条回答 默认 最新

  • 忘崽 2022-03-27 19:46
    关注

    已解决,您首先要根据valid_node_quantity这个变量判断有多少个block,判断node[]数组的长度

    void filter(const nlink_parser::LinktrackAoaNodeframe0::ConstPtr &msg)
    {

    ROS_INFO("标签订阅到的消息:角色:%d,id值:%d,供电电压:%.2f",msg->role,msg->id,msg->voltage);
    ROS_INFO("------------------------------------------------");
    ROS_INFO("size:%d",msg->nodes.size());
    
    if(msg->nodes.size()>0)
    {
        cout<<"距离:"<<msg->nodes[0].dis<<endl;
        cout<<"角度:"<<msg->nodes[0].angle<<endl;
        ROS_INFO("距离:%.2f",msg->nodes[0].dis);
    }
    
    else if(msg->nodes.size()==0)
    {
        cout<<"error"<<endl;
    }
    else{
        return;
    }
    

    }

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 4月4日
  • 已采纳回答 3月27日
  • 创建了问题 3月27日

悬赏问题

  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站
  • ¥50 成都蓉城足球俱乐部小程序抢票
  • ¥15 yolov7训练自己的数据集
  • ¥15 esp8266与51单片机连接问题(标签-单片机|关键词-串口)(相关搜索:51单片机|单片机|测试代码)