OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5688|回复: 14

关于结构体,指针的问题

[复制链接]

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
发表于 2015-3-6 14:22:23 | 显示全部楼层 |阅读模式
5金钱
[mw_shl_code=c,true] #include "stdint.h" // 命令结构体 typedef struct _Command_Block { uint8_t eCommandType; uint8_t Databuf[64]; }Command_Block_t; //试运行命令 数据类型 typedef struct _TestRun_CmdData { uint8_t Test_Mode; // 1 模式 uint8_t Test_Run; // 1 开始测试 double Speed; // 速度 }Test_Run_CmdData_t; // 主程序 void test_main(void) { Command_Block_t NewCommand; Test_Run_CmdData_t* NewCmd; // 主要是想将NewCmd 指向 NewCommand.Databuf NewCmd = (Test_Run_CmdData_t*)&NewCommand.Databuf[0]; //指针指Databuf存 NewCmd->Test_Mode = 1; NewCmd->Test_Run = 1; NewCmd->Speed = 0.05; // 这里会跳入 HardFault_Handler if(NewCmd->Test_Run == 0) return; }[/mw_shl_code]
1.请问问题出在那里????
2.如果我把uint8_t Databuf[64];数组替换为uint8_t* Pbuf 。Pbuf初始化的时候指向一个全局数组变量Cmd[64]。再执行下面的操作就不会出问题。 这个又是什么原因???

[mw_shl_code=c,true][/mw_shl_code]

最佳答案

查看完整内容[请看2#楼]

 回复【9楼】 ianhom : --------------------------------- 问题暂时得到解决。 不过我验证了我最初的想法,应该是结构体的字节对齐原因。我把结构体内变量定义的顺序改一下就可以了。对于为什么用全局数组就能解决还是有点困惑。 。 我解释的不太通,我放一个链接在这里,供大家参考。http://www.cnblogs.com/longlybits/articles/2385343.html 不过还是需要大神来解释一下,解答一下疑惑。 请问用uint16_t有啥 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
 楼主| 发表于 2015-3-6 14:22:24 | 显示全部楼层
 回复【9楼】 ianhom :
---------------------------------

问题暂时得到解决。
不过我验证了我最初的想法,应该是结构体的字节对齐原因。我把结构体内变量定义的顺序改一下就可以了。对于为什么用全局数组就能解决还是有点困惑。 。
我解释的不太通,我放一个链接在这里,供大家参考。http://www.cnblogs.com/longlybits/articles/2385343.html
不过还是需要大神来解释一下,解答一下疑惑。

请问用uint16_t有啥问题,还有全局数组?   我暂时还没有发现。

代码只要调换一下定义顺序就可以执行了。
[mw_shl_code=c,true]#include "stdint.h" // 命令结构体 typedef struct _Command_Block { uint8_t Databuf[64]; // 关键问题在这里!! 由于定义结构体时结构体所处的地址能被其中最大元素的大小(?)整除! // 所以这个可以作为自定义结构体的起始地址 uint8_t eCommandType; // 结构体起始地址 不应该是一个不规则的地址(?)。若在这个变量后面,那么Databuf的起始地址为x+1 // 由于下面的结构体含有double类型,则要求结构体的起始地址必须能被8整除,显然x+1不能整除8 所以出错! }Command_Block_t; //试运行命令 数据类型 typedef struct _TestRun_CmdData { uint8_t Test_Mode; // 1 模式 uint8_t Test_Run; // 1 开始测试 double Speed; // 速度 }Test_Run_CmdData_t; // 主程序 void test_main(void) { Command_Block_t NewCommand; Test_Run_CmdData_t* NewCmd; // 主要是想将NewCmd 指向 NewCommand.Databuf NewCmd = (Test_Run_CmdData_t*)&NewCommand.Databuf[0]; //指针指Databuf存 NewCmd->Test_Mode = 1; NewCmd->Test_Run = 1; NewCmd->Speed = 15; // 不会跳入了 HardFault_Handler if(NewCmd->Test_Run == 0) return; } [/mw_shl_code]



说到底HardFault_Handler 还是一样的错误,指针指向了一个不正确的地址,这个地址要求是必须能被8整除的。


这是我现在的见解,欢迎指正啊!
回复

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2015-3-6 14:22:24 | 显示全部楼层
在KL25芯片上IAR环境进行了仿真:
为了方便分析,我讲double改成了uint16_t,如图一,Command_Block_t的结构成员顺序不变,当申请结构体实体NewCommand的时候,地址是32位对齐,所以结构体的首地址就落在了0x20002FA8上,而结构体第一个成员是一个8位的变量,第二个成员才是数组,这时这个数据的首地址为0x20002FA9。随后对这个数组的首地址进行了指针类型转换,然后对这三个成员进行赋值,可以发现Test_Mode的地址理所当然地为0x20002FA9,Test_Run的地址为0X20002FAA,赋值情况可在汇编上看出(左边红框数据对红框汇编)。然而在编译器编译的时候,对于成员speed就是结构体的首地址加0x02(见蓝色汇编),也就是说speed的地址位0x20002FAB,注意这里speed是16位的,这个时候将16位的数据赋值到不对齐的地址上,就会出现hard fault。




基于以上分析,我们将Command_Block_t结构体里的成员顺序调整如下图黄框所示,数组为第一个成员,这样进行同样的流程就会发现,Test_Mode在地址0x20002FA8,Test_Run在地址0x20002FA9,之前出错的speed这在地址0X20002FAA这个16位对齐的地址上,请比较红蓝框内存数据和汇编。



然后继续执行以前会出现hard fault的语句,发现没有问题,0x20002FAA成功赋值“1”,见下图蓝框



那为什么同样的数据结构顺序,用数组就出错,但用指针的方式就没有出错呢。原因是当我们单独申请一个64个uint8_t类型的数组的时候,这个地址是对齐的,见下图,Test_Mode的地址为0x20002FA8,speed地址也是16位的0x20002FAA,情况同上,所以没有出现问题,内存数据和汇编请看下图左边红蓝框



也就是说,用指针的方式能成功是因为数组是单独申请的,地址是对齐的,所以没有问题,那我们继续验证,如果我们让指针不对齐,是不是就出现最初的问题呢,这次我们不将数组的首地址给NewCmd,而是吧数据的第二个元素的地址给NewCmd。这时会发现Test_Mode的地址位0x20002FA9,speed的地址为0x20002FAB这个不对齐的地址,情况和最初的分析一直,继续运行同样会出现hard Fault的问题,详见下图



那么我们再回到double的情况,通过sizeof可知在我的平台上double占八个字节,我们重新调整两个结构体内成员的排序,如下图黄匡,避免了未对齐的问题,依然用最初的结构体内数组的方式,运行OK,详见下图



所以我觉得问题出现在数据对齐上,正常的同类型同size的数据做类型转换应该问题不大,但要注意结构体内成员的安排(这个我要好好研究一下),同时要注意不同类型数据结构指针转换时,编译器会按照转化后的数据格式(位置)进行内存操作,而这时出现不对齐的数据操作,就会操作hard fault。
以上分析未必全面,恳请高手进一步点拨。
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复

使用道具 举报

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
 楼主| 发表于 2015-3-6 14:23:12 | 显示全部楼层
问题主要是在第35行  执行后会跳入HardFault_Handler。     编译器是KEIL。
说白了,就是结构体里定义一个足够长的数组, 我想用我需要的数据类型指针去访问这个数组。  求解答!
回复

使用道具 举报

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
 楼主| 发表于 2015-3-6 14:24:20 | 显示全部楼层
有兴趣的同学,可以吧我这段代码复制过去测试一下,研究一下到底是什么问题?
回复

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2015-3-6 16:46:54 | 显示全部楼层
试了一下KL25平台+iar也是一样的情况,看了下汇编结果也不一样,

用数组出错的汇编



用指针指向数组不出错的汇编



求高手看看

机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复

使用道具 举报

16

主题

59

帖子

0

精华

初级会员

Rank: 2

积分
147
金钱
147
注册时间
2014-11-4
在线时间
3 小时
发表于 2015-3-6 18:34:23 | 显示全部楼层
一般情况HardFault_Handler错误,主要原因是:
1、内存溢出或者访问越界。这个需要自己写程序的时候规范代码,遇到了需要慢慢排查。

2、堆栈溢出。增加堆栈的大小,或者你把Databuf[64]改小点试试
回复

使用道具 举报

16

主题

59

帖子

0

精华

初级会员

Rank: 2

积分
147
金钱
147
注册时间
2014-11-4
在线时间
3 小时
发表于 2015-3-6 18:38:27 | 显示全部楼层
因为你定义的全局变量数组保存在数据段,不会出现堆栈溢出的现象,所有可以正常运行
回复

使用道具 举报

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
 楼主| 发表于 2015-3-6 19:47:42 | 显示全部楼层
回复【4楼】ianhom:
---------------------------------
多谢测试,同求大神解答一下,如果要靠定义全局数组来的实现的话,不能达到我要的效果。 我想一个命令一个实体。纠结中。。
回复

使用道具 举报

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
 楼主| 发表于 2015-3-6 19:50:38 | 显示全部楼层
回复【6楼】Beans:
--------------------------------
我把数组调小到了16还是出现同样的问题。  当我把double换成uint16_t一类的非浮点型数据时,就不会出现这个问题。   我在想是不是数据字节对齐的原因,可还是想不通
回复

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2015-3-6 20:05:20 | 显示全部楼层
回复【8楼】jarming:
---------------------------------
全局变量我也试验过,同样有问题
uint16_t的实验我也做过,同样有问题
这里的hardfault 不太像内存溢出或栈溢出,我检查了堆和栈都没看出问题,我经常遇到hardfault的情况是非法指针操作,检查了两种情况的汇编还是有差别,汇编不是很好,还在啃,求汇编高手指点
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2015-3-6 21:26:15 | 显示全部楼层
回复【2楼】jarming:
---------------------------------
实验了一下,大致找到原因,你先把结构体改成这样在试试
    typedef struct _Command_Block
    {
        uint8_t        Databuf[64];
        uint8_t        eCommandType;
    }Command_Block_t;
     
    typedef struct _TestRun_CmdData
    {
        double  Speed;         // 速度
        uint8_t Test_Mode;  // 1 模式
        uint8_t Test_Run;     // 1 开始测试
        
    }Test_Run_CmdData_t;
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2015-3-6 22:04:03 | 显示全部楼层
回复【11楼】jarming:
---------------------------------
看一下我的分析,不知能不能解决你的问题
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复

使用道具 举报

3

主题

13

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2015-1-29
在线时间
1 小时
 楼主| 发表于 2015-3-6 22:22:19 | 显示全部楼层


经过这么一分析,清晰了不少。
今天这个真是学习了。  
谢谢ianhom的热情帮助!!!!!!!!!!!!

回复

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2015-3-7 07:28:46 | 显示全部楼层
回复【14楼】jarming:
---------------------------------
相互学习,通过你这个问题我也学习了不少
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-25 07:10

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表