C语言定义数组起始地址对齐方式(IAR C99 Kinetis K66)
##前言##
前几天,主管找我问关于定义变量起始地址对齐方式的问题。下面介绍一下问题:上位机生成了一个参数数组 char para[36541] = {…} ;数组比较大,已经内建好了结构体,只要按照指针指到头部地址即可正常读取出来,但是由于处理器读取int 或者 float 数据类型比较要求数据地址是四字节对齐的,而我们这个数组存放在内存中是以两个字节对齐的,这就导致了CPU无法读取数据并死机在了读取float数据类型处;
主管提出,能否直接指定该数组的存在于内存中的起始地址?我一想,确实是,只要头地址对了那就啥问题也没有了。
主管又补充了一句,他测试过了 #pragma pack(4) 这种编译命令,实际效果是没效果,于是我上网查了下,这个命令确实是数据对齐的命令,但是只是对于结构体内部。对于数组头地址,我开始觉得有点挑战了;
而且方法要简单,不要影响程序的运行效率问题;
##问题尝试
今天在处理上次开会中提到的内存分配的问题,改着改着忽然就灵光一闪,觉得这个问题应该是可以解决的:
我拿出了自己使用的比较利索的C语言工具->IAR workbench for ARM; 测试平台当然还是最熟悉的Kinetis 系列的 K66 啦!
先定义以下变量:测试看看存放于内存中的位置,代码如下:
char aa[2] = {1,2};
查看地址的方式有两个:
- 通过IAR编译器生成的MAP文件进行查找
- 就是通过IAR DEBUG工具中的 watch工具进行查看
而我这种偷懒王必须采用第二种方式啊,多么直观,哈哈哈哈;
如下是我通过IAR Debug watch中查看到的相关信息:
Expression | Value | Location | Type |
---|---|---|---|
aa | <array>"??" | 0x1FFF0070 | char[2] |
bb | <array>"??" | 0x1FFF0072 | char[2] |
可以看出上面aa存放地址是 0x1FFF0070 而bb的地址存放在0x1FFF0072地址上,bb的起始地址是一个2字节对齐的地址,并非四地址对齐的地址。
##明确目标,向前进发
准备修改bb的首地址到一个四字节对齐的地址上,或者八字节对齐的地址上
目标明确过后就是尝试了,我首先尝试了下主管说过的
#pragma pack(x) ;
这类编译命令,不出意外,一点影响都没有;
直到今天,一个偶然中想到了是否可以使用IAR 链接时候使用的ICF文件(链接文件)进行处理;
好,说做就做:开始尝试,写了一个简单的链接代码测试一下:
//define block to place test value define symbol m_data_start = 0x1FFF0000;define symbol m_data_end = 0x1FFFFFFF;define memory mem with size = 4G; define region test_region = mem:[from m_data_start to m_data_end];define block test_block with alignment = 4 {section .test};place in test_region {block test_block};
这里定义了一下链接文件(*.icf)将 .test这个段放在了0x00000410 ~ 0x1FFFFFFF这个区段中以四字节对齐的地址上;
然后回去在变量定义中修改成如下代码:
char aa[2] = {1,2};#pragma location = ".test"char bb[2] = {3,4};
这里意思是数组aa依然按照编译器缺省规则编译,而数组bb则存放到 .test段中;
究竟是否成功呢?打开IAR Debug watch:惊喜啊,真的是啊:
以下是4字节对齐aa、bb首地址
Expression | Value | Location | Type |
---|---|---|---|
aa | <array>"??" | 0x1FFF0070 | char[2] |
bb | <array>"??" | 0x1FFF04A8 | char[2] |
好家伙,真的是成了四字节对齐的地址了(0x1FFF04A8 % 4 == 0);为了确保是这个样子,多实验几下看看,看看是不是对的,于是将链接中关于对齐部分代码修改如下:
define block test_block with alignment = 8 {section .test};
再使用IAR debug watch查看一下地址:
以下是8字节对齐的aa、bb首地址
Expression | Value | Location | Type |
---|---|---|---|
aa | <array>"??" | 0x1FFF0070 | char[2] |
bb | <array>"??" | 0x1FFF04A8 | char[2] |
地址竟然和上面一样,使用计算器计算一下发现(0x1FFF04A8 % 8 == 0),确定是8字节对齐的。
我心里还是有点疑虑,看看16、32字节对齐会有啥样子?
以下是16字节对齐的aa、bb首地址
Expression | Value | Location | Type |
---|---|---|---|
aa | <array>"??" | 0x1FFF0070 | char[2] |
bb | <array>"??" | 0x1FFF04B0 | char[2] |
新地址计算一下(0x1FFF04B0 % 16 == 0 )
以下是32字节对齐的aa、bb首地址
Expression | Value | Location | Type |
---|---|---|---|
aa | <array>"??" | 0x1FFF0070 | char[2] |
bb | <array>"??" | 0x1FFF04C0 | char[2] |
新地址计算一下(0x1FFF04C0 % 16 == 0 )
##结论
通过这一系列的尝试。发现ICF文件(链接文件)中的块alignment关键字可以使块的首地址按照2^x(2,4,8,16,32…)方式对齐;而我们定义这一个块就存放一个变量,那么这个变量的首地址的对齐方式就是一个可控的值。
##展望
由于项目中使用的参数文件是上位机生成的一个数组,按道理这个参数是不需要被被修改的,本着提高CPU的效率考虑(CPU复位时候程序初始化需要搬运.data段到RAM中),我们可以定义到该变量到CODE中成为一个常量表,可以提高系统启动的速率;
以下为改进的icf文件关键代码:
//define block to place test value define symbol m_text_start = 0x00000410;define symbol m_text_end = 0x001FFFFF;define memory mem with size = 4G; define region test_region = mem:[from m_text_start to m_text_end];define block test_block with alignment = 4 {section .test};place in test_region {block test_block};
以下为改进了的测试数组定义
char aa[2] = {1,2};#pragma location = ".test"const char bb[2] = {3,4};
以下是通过IAR Debug Watch 所观察到的变量数据:
Expression | Value | Location | Type |
---|---|---|---|
aa | <array>"??" | 0x1FFF0070 | char[2] |
bb | <array>"??" | 0x00013748 | char[2] |
可以发现该地址存在于flash空间中,仍然是一个4字节对齐的地址,成功,哈哈哈哈,这次任务完成;
##寄语
楼主文笔特别差,很多时候表词不达意,或者用词错误,亦或者对于东西理解不深入等等。但是希望我的分享能够解决你的实际问题。也希望你阅读过程中发现对文章的技术或者其他方法有怀疑的提出反馈,一起讨论学习,一起进步。谢谢阅读。
C语言定义数组起始地址对齐方式(IAR C99 Kinetis K66)相关推荐
- 代码示例:Java中定义数组的三种方式
在Java中,数组的定义有三种方式,其基本语法格式如下: 数组类型[] 数组名 = new 数组类型[数组长度]; 数组类型[] 数组名 = new 数组类型[]{数组元素0,数组元素1,...}; ...
- java中定义数组的3种方式
直接上代码和截图 public static void main(String[] args) {// 定义数组的3种方式// 第一种String[] role = new String[3];rol ...
- C语言中数组首地址和数组第一个元素的地址有什么区别
C语言中数组首地址和数组第一个元素的地址关系如下: 1.它们的地址值是相等的. 2.第1个元素的地址如果是p,则p+1就是第2个元素的地址. 3.数组的首地址如果是p,则p+1就跳过这个数组而指向这个 ...
- Avalon-MM接口地址对齐方式浅析
在Avalon-MM接口规范中,有两种地址对齐方式:Native Address Alignment和Dynamic Bus Sizing. riple 引入地址对齐的机制是为了解决主设备和从设备数据 ...
- c语言数组形式,c定义数组_C语言定义数组的几种形式
摘要 腾兴网为您分享:C语言定义数组的几种形式,中日翻译,中国天气,智慧医疗,知富等软件知识,以及课观银行帮,驾照查分app,侍魂,cad转pdf转换器,云南山歌,指南针运动,韶关人社,我的世界拔刀剑 ...
- Java 定义数组的三种方式,int...x动态参数列表
定义数组的三种方式 以 int型 的一维数组为例,说说三种定义方式 int[] arr = new int[3]; 这是最常用的方式,定义时就含有默认值,可以后续赋值 public class Dem ...
- c语言定义数组6,C语言教程6数组.ppt
C语言教程6数组.ppt #include main( ){ char name[13]; int i; for (i =0; i <=12; i ++)scanf(" %c" ...
- c语言定义数组a10 指定各元素,C语言填空题.doc
C语言填空题.doc 二.填空题1.C 语言只有 32 个关键字和 9 种控制语句.2.每个源程序有且只有一个 main 函数,系统总是从该函数开始执行 C 语言程序. 3.C 语言程序的注释可以出现 ...
- js中定义数组的两种方式
js中定义数组: 注意事项: 1.在js中定义数组,不用担心数组角标越界的问题,可以自动扩容. 2.在js中,数组是可以定义任何数据类型的. 1.指定数组长度 运行结果: 2.简写方式:
最新文章
- 扛住 100 亿次请求?我们来试一试
- openshift_OpenShift上的Java EE工作流(技术提示#64)
- commons-lang常用方法
- Flask的csrf_token的用法
- java httpclient 异步请求_Java利用httpasyncclient进行异步HTTP请求
- 【JUC】第二章 线程间通信、集合的线程安全
- 用计算机进行实时自动采集,《大学计算机基础》基础部分练习题_附件1
- python可视化界面开发实例-Python可视化界面编程入门
- EM算法原理详解与高斯混合模型
- 带通滤波器作用和用途_带通滤波器是什么,带通滤波器的作用
- 【Struts】ActionForm
- Python-Excel报表自动化生成报表(二)
- 关于travis scott的网名_【游戏网名】微信情侣名字情侣专用2020 霸气秀恩爱的情侣网名...
- 又涨了?2022年1月程序员平均薪资15052元,高薪还能持续多久?
- 电视盒子刷linux树莓派,用树莓派4B打造纯ATV 10代替电视盒子
- 逻辑回归(神经网络Sigmod激活函数,计量logit模型)
- 43、总建筑面积大于20000㎡的地下或半地下建筑的防火要求
- Windows10文件夹打不开提示位置不可用的解决方案
- Latex 论文插入copyright
- 全栈之巅-NodeJs(AdonisJs)+VueJs开发带完整后台管理UI的博客系统笔记