[STM32F4] STM32F407 结构体指针赋值失败。。。好奇怪啊~~~

[复制链接]
1599|11
 楼主| squall001 发表于 2020-8-27 00:34 | 显示全部楼层 |阅读模式
本帖最后由 squall001 于 2020-8-27 01:01 编辑

大概意思是我做了个队列,在VS上面跑没问题,在单片机上面跑就出问题了,最终我把问题定位在了一条给指针赋值的语句上,结果就是赋值失败,但是诡异的是失败是有概率的,并不是每次赋值都失败,只是有时候失败。。。
~~~下面直接上代码
下面是头文件内的代码,声明结构体

  1. typedef unsigned char QElemType;
  2. typedef struct QNode //节点结构
  3. {
  4.         QElemType data;
  5.         struct QNode *prev;//指向上一个节点
  6. }QNode,*QueuePtr;

  7. typedef struct Link_queue//队列的链表结构
  8. {
  9.         QueuePtr front;
  10.         QueuePtr rear; //队列的头、尾指针
  11. }LinkQueue;


下面是C文件相关代码段

  1. /*
  2. *********************************************************************************************************
  3. *        功能说明: 从队列里面获取最先进队的数据,并删除该节点
  4. *        形    参: data  把数据存放在该指针地址
  5. *        返 回 值: Q_Status Q_ERR_NO 返回错误,队列没有数据  Q_ERR_OK成功执行
  6. **********************************************************************************************************/
  7. Q_Status GetQueue(LinkQueue* Q,QElemType* data)
  8. {
  9.         QueuePtr p=NULL;
  10.         p=Q->rear;                //这里赋值 有时候会失败,而且是确定Q->rear为非空的情况下
  11.         if (Q->rear == NULL && Q->front == NULL)
  12.         {
  13.                 return Q_ERR_NO;
  14.         }
  15.         if (Q->rear->prev != NULL)        //表示当前队列节点数 > 1
  16.         {
  17.                 ////以下if语句仅做调试用
  18.                 if(p==NULL)
  19.                 {
  20.                         p=Q->rear;                //强行再次赋值
  21.                         return Q_ERR_NO;
  22.                 }
  23.                 ///////////////////////////
  24.                 *data = Q->rear->data;                                        //把值赋给传进来的参数
  25.                 Q->rear = Q->rear->prev;        //把队列指针的尾节,点前移一个节点

  26.                 free(p);        //释放内存
  27.                 return Q_ERR_OK;
  28.         }
  29.         else if (Q->rear != NULL)        //这里表示队列只有一个节点
  30.         {               
  31.                 ////以下if语句仅做调试用
  32.                 if(p==NULL)
  33.                 {
  34.                         p=Q->rear;
  35.                         return Q_ERR_NO;
  36.                 }
  37.                 ///////////////////////////
  38.                 *data = p->data;                        //把值赋给传进来的参数
  39.                 Q->front = Q->front->prev;
  40.                 Q->rear = Q->rear->prev;

  41.                 free(p);

  42.                 return Q_ERR_OK;
  43.         }
  44.         else
  45.         {
  46.                 //这里表示队列没有节点了 暂时不做任何操作
  47.                 return Q_ERR_NO;

  48.         }

  49. }


问题
问题就出在这函数内的第二句p=Q->rear;这个Q是结构体指针,申请的全局变量,p就是函数内申请的临时指针变量,类型同样也是定义的那种结构体指针。根据队列里面是否为空,那么这个指针有时候有值,有时候为NULL,这个正常。
其他都不用讨论了,要说的情况就是在我确定Q->rear不为NULL的情况下,执行语句p=Q->rear;结果p还是为null,下面直接上图
1.png
下图是相应的汇编代码
2.png


这种赋值失败我确实不知道怎么解决了,我用的MDK为keil 5.27,HAL库

一周一天班 发表于 2020-8-27 09:02 来自手机 | 显示全部楼层
优化改为00

评论

已经是00了  发表于 2020-8-27 10:18
sonicll 发表于 2020-8-27 09:15 | 显示全部楼层
开头这句 QueuePtr p=NULL; 加个 volatile 试试?

评论

@sonicll :你看函数代码就知道,在这个函数里面R5这个寄存器在后期没有做任何改变的,问题解决了,看6楼帖子  发表于 2020-8-27 11:30
@squall001 :你的截图里只能看到p这个变量被放在了R5里,并没有放在内存里,不知道后面R5有没有被改变。加volatile关键字,应该会把p值存在栈里,每次用的时候都会从栈里读取  发表于 2020-8-27 10:48
不是这个问题,因为从汇编可以看到,系统是给它分配了内存的  发表于 2020-8-27 10:18
ayb_ice 发表于 2020-8-27 09:25 | 显示全部楼层
本帖最后由 ayb_ice 于 2020-8-27 09:28 编辑

应该不是这里的问题,
可能是优化的原因,
先把那个p改成全局变量试试,再查找问题
if (Q->rear == NULL && Q->front == NULL)
这里应该是 ||逻辑吧
酱油兑啤酒 发表于 2020-8-27 10:43 | 显示全部楼层
你只是执行到p = Q->rear,但是并没有执行这个语句,你的p肯定是0的吧

评论

并不是。。。函数开始就执行了  发表于 2020-8-27 11:31
 楼主| squall001 发表于 2020-8-27 11:28 | 显示全部楼层
ayb_ice 发表于 2020-8-27 09:25
应该不是这里的问题,
可能是优化的原因,
先把那个p改成全局变量试试,再查找问题

确实不是这里的问题,今天早上来公司,突然灵感来了,解决了此问题,在这里还是怪自己没把外围条件给说清除
首先有个前提条件,在增加队列的时候,是由外部中断进行的(CAN命令添加的),而执行GetQueue函数是在主函数while里面的,所以任何时候都有可能被打断,那么问题就来了

问题的发生如下:
一般情况下,队列里面是空的,那么Q->rear也就是空的,在主循环内不断的去执行GetQueue函数,用意在于提取队列里面的值,当然如果队列是空,会经过if (Q->rear == NULL && Q->front == NULL)判断后直接返回Q_ERR_NO。
当然每次都是执行了p=Q->rear这条语句的。但是如果是在执行完这条语句过后,然后外部中断来了,这时候在中断内部做了添加队列节点操作,那么这个时候Q->rear的值已经改变了,那么再次执行if (Q->rear == NULL && Q->front == NULL)这条语句就会跳过,进而执行到下面的语句,这样就会出现上图的现象,看起来Q->rear是有值的,而p是没有值。。。。
对于现在是队列,简单的处理就是把这个p=Q->rear赋值到判断条件内部,而不是在函数开始的地方就给它赋值。。另外一个更为保险的做法就是把外部中断使能关闭,执行完了这些操作后再打开,这样只要中断标志位在那里,即使期间有中断来了,它还是会挂起,等中断使能恢复后会自动跳进中断,不影响。。。

这种原子操作本来在C语言里面都是一个弱点,没有类似互斥锁这种概念在里面,所以会出现类似问题,大家以后要小心
 楼主| squall001 发表于 2020-8-27 11:34 | 显示全部楼层
最后上修改后的代码

  1. /*
  2. *********************************************************************************************************
  3. *        功能说明: 从队列里面获取最先进队的数据,并删除该节点
  4. *        形    参: data  把数据存放在该指针地址
  5. *        返 回 值: Q_Status Q_ERR_NO 返回错误,队列没有数据  Q_ERR_OK成功执行
  6. **********************************************************************************************************/
  7. Q_Status GetQueue(LinkQueue* Q,QElemType* data)
  8. {

  9.         QueuePtr p=NULL;
  10.         if (Q->rear == NULL && Q->front == NULL)
  11.         {
  12.                 return Q_ERR_NO;
  13.         }
  14.         if (Q->rear->prev != NULL)        //表示当前队列节点数 > 1
  15.         {
  16.                 p=Q->rear;                                                                //一定要在这里赋值,用以确定Q->rear是有值的
  17.                 *data = Q->rear->data;                                        //把值赋给传进来的参数
  18.                 Q->rear = Q->rear->prev;        //把队列指针的尾节,点前移一个节点

  19.                 free(p);        //释放内存
  20.                 return Q_ERR_OK;
  21.         }
  22.         else if (Q->rear != NULL)        //这里表示队列只有一个节点
  23.         {               
  24.                 p=Q->rear;                                                        //一定要在这里赋值,用以确定Q->rear是有值的
  25.                 *data = p->data;                                //把值赋给传进来的参数
  26.                 Q->front = Q->front->prev;
  27.                 Q->rear = Q->rear->prev;

  28.                 free(p);

  29.                 return Q_ERR_OK;
  30.         }
  31.         else
  32.         {
  33.                 //这里表示队列没有节点了 暂时不做任何操作
  34.                 return Q_ERR_NO;

  35.         }

  36. }


您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

14

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部