打印
[STM32F4]

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

[复制链接]
1191|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 squall001 于 2020-8-27 01:01 编辑

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

typedef unsigned char QElemType;
typedef struct QNode //节点结构
{
        QElemType data;
        struct QNode *prev;//指向上一个节点
}QNode,*QueuePtr;

typedef struct Link_queue//队列的链表结构
{
        QueuePtr front;
        QueuePtr rear; //队列的头、尾指针
}LinkQueue;


下面是C文件相关代码段

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

                free(p);        //释放内存
                return Q_ERR_OK;
        }
        else if (Q->rear != NULL)        //这里表示队列只有一个节点
        {               
                ////以下if语句仅做调试用
                if(p==NULL)
                {
                        p=Q->rear;
                        return Q_ERR_NO;
                }
                ///////////////////////////
                *data = p->data;                        //把值赋给传进来的参数
                Q->front = Q->front->prev;
                Q->rear = Q->rear->prev;

                free(p);

                return Q_ERR_OK;
        }
        else
        {
                //这里表示队列没有节点了 暂时不做任何操作
                return Q_ERR_NO;

        }

}


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

下图是相应的汇编代码



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

使用特权

评论回复
沙发
一周一天班| | 2020-8-27 09:02 | 只看该作者
优化改为00

使用特权

评论回复
评论
squall001 2020-8-27 10:18 回复TA
已经是00了 
板凳
sonicll| | 2020-8-27 09:15 | 只看该作者
开头这句 QueuePtr p=NULL; 加个 volatile 试试?

使用特权

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

应该不是这里的问题,
可能是优化的原因,
先把那个p改成全局变量试试,再查找问题
if (Q->rear == NULL && Q->front == NULL)
这里应该是 ||逻辑吧

使用特权

评论回复
5
酱油兑啤酒| | 2020-8-27 10:43 | 只看该作者
你只是执行到p = Q->rear,但是并没有执行这个语句,你的p肯定是0的吧

使用特权

评论回复
评论
squall001 2020-8-27 11:31 回复TA
并不是。。。函数开始就执行了 
6
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语言里面都是一个弱点,没有类似互斥锁这种概念在里面,所以会出现类似问题,大家以后要小心

使用特权

评论回复
7
squall001|  楼主 | 2020-8-27 11:34 | 只看该作者
最后上修改后的代码

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

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

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

                free(p);

                return Q_ERR_OK;
        }
        else
        {
                //这里表示队列没有节点了 暂时不做任何操作
                return Q_ERR_NO;

        }

}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

14

帖子

0

粉丝