嵌入式开发C语言问题详解
  嵌入式系统的C语言开发中,经常遇到这样那样的问题。有些问题可能很快就能到原因,但是有些问题必须有一定的经验积累才能快速到原因。
  一、由编译优化引起的问题
  例1、编译后的汇编语言处理逻辑跟C语言处理逻辑不一致
  由于编译器的原因,在设置了编译优化的情况下,编译后有些代码的逻辑会发生变化。这种情况下会发生很奇怪的问题,一些函数的处理结果跟预想的不一致,而检查代码又看不出什么问题。
  这种问题的解决办法一般是在充分分析软件处理逻辑,确认处理上没问题后,把编译后的列表文件(*.lis)和C语言处理逻辑逐行对照。把不一致的地方出来,并寻修正对策。
  例2、编译后的一些处理被优化了
  这种问题经常发生在硬件寄存器的操作上。对于硬件而言,每一次读写操作可能都有特定
的含义:某些硬件寄存器要求读一下才能做后续其他处理;而某些寄存器要连续写几次。比如下面的情形:
  #define TSTREG (unsigned char *) 0x00C00032 /*TEST REGISTER */
  unsigned char *pTSTR;
  pTSTR = TSTREG;
  *pTSTR = 0x01; //这个操作很容易被编译器优化掉。
  *pTSTR = 0x02;
  ……
  作为对策之一,可以在定义变量时加上volatile关键字。比如:
  volatile unsigned char *pTSTR;
  volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变
化,与该变量有关的运算,不要进行编译优化,以免出错。使用volatile变量的几个场景:
  1)中断服务程序中修改的供其它程序检测的变量需要加volatile。
  2)多任务环境下各任务间共享的标志应该加volatile。
  3)存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义。
  二、由字节对齐引起的问题
  一个结构体变量定义完之后,其在内存中的存储并不一定等于其所包含元素的宽度之和。因为这里涉及到字节对齐的问题。
  结构体中元素的对齐基本上遵循两个基本原则:
  原则一:结构体中元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每一个元素放置到内存中时,它都会认为内存是以它自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始(以结构体变量首地址为0计算)。
  原则二:在经过第一原则分析后,检查计算出的存储单元是否为所有元素中最宽的元素的长度的整数倍。若是,则结束;若不是,则补齐为它的整数倍。
  每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数或边界调整数)。通常,可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
  #pragma pack(n) //编译器将按照n字节对齐
  #pragma pack() //编译器将取消自定义字节对齐方式
  在#pragma pack(n)和#pragma pack()之间的代码按n字节对齐。但是成员对齐有一个重要的条件,即每个成员按照自己的对齐方式对齐; 也就是说虽然指定了按n字节对齐,但并不是所有的成员都以n字节对齐。对齐的规则是:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n字节)中较小的一个对齐,即min(n,sizeof(item)),并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。
  以瑞萨SH7145F CPU和XASS-V编译器为例,根据XASS-V帮助文件,对于SH7145F,
其结构体成员的默认对齐系数如下:
  char:1
  short:2
  int:4
  long:4
  float:4
  long long:4
  double:4
  指针:4
  结构体/联合体:4
  数组:根据类型而定
  在编译的时候可以通过设置 /bond = n, n=4(一般情况),来指定边界调整系数。因此,实际采用/bond=n中的n和上述默认对齐系数中的较小者。
  由于跟特定编译器有关,所以下面的例子仅限XASS-V编译器,目标CPU是瑞萨SH7145F。
  例1、假设 /bond=4,进行如下定义:
  typedef unsigned char UCHAR;
  /* 结构体定义*/
  typedef struct{
  union{
  UCHAR SO_bit7;
  UCHAR DMY0_bit56;
  UCHAR LNO_bit04;
  } DL; /* 预想BYTE0 */
  UCHAR ORDN;
  union{
  UCHAR RW_bit7;
  UCHAR DTNO_bit06;
  }RD; /* 预想BYTE1 */
  UCHAR ADRH; /* 预想BYTE2 */
  UCHAR ADRL; /* 预想BYTE3 */
  UCHAR DATA; /* 预想BYTE4 */
  }stTEST;
  /* 变量定义*/
  UCHAR TEST[6];
  stTEST *pTEM=( stTEST *) TEST;
  这样执行后,pTEM->ADRH并不是对应TEST [3],导致了数据处理错误。
  原因分析:
  由边界调整数决定,union只能在4的倍数的地址上存放;且UNION类型要占用4*X Byte,故后面有3Byte的Dummy。即UNION{xxx}DL占用了4Byte。
  依次类推,整个结构体元素的内存分布如下:
  UNION{xxx}DL; /* Byte0~Byte3: 后3Byte Dummy*/
  UCHAR ORDN; /* Byte4~Byte7: 后3Byte Dummy*/
  UNION{xxx}RD; /* Byte8~Byte11: 后3Byte Dummy*/
  UCHAR ADRH; /* Byte12*/
  UCHAR ADRL; /* Byte13*/
  UCHAR DATA; /* Byte14~Byte15: 后1Byte Dummy*/
  Byte5~Byte7的Dummy是因为UNION成员必须在4的倍数的地址上存放。
  Byte15的Dummy是整个结构体大小必须是4的倍数。
  所以sizeof(stTEST)=16, pTEM->ADRH对应为Byte12,不是预想的.TEST[3]。
  三、由变量类型不匹配引起的问题
  例1、循环变量溢出
  UCHAR i = 0x00;
  /* 版本1:100个循环 */
  for(i=0;i<100;i++) { /* 处理:略*/}
  /* 版本2:1000个循环 */
  for(i=0;i<1000;i++) { /* 处理:略*/}
  一开始需求是100个循环,而后面需求变更为1000个循环,但忘记修改循环变量类型。以为UCHAR的有效范围是0~255,显然不满足版本2的要求。这种情况会发生在循环变量定义的位置距离循环体比较远的时候,在无意识中忽略了。
  四、由数组下标越界引起的问题
嵌入式系统开发是什么
  常见的是指定的数组下标超过了数组最大有效下标。很多情况下不会导致程序奔溃,但是取出的数据显然是不正确的。
  五、由字节序引起的问题
  字节序主要体现大于1Byte的数据的存储方式上。对于CPU而言,有MSB FIRST和LSB FIRST两种存储方式。MSB指Most Significant Bit,即最高有效位;LSB指Lest Significant Bit,即最低有效位。简单地说,MSB FIRST就是高位优先存储,即高位存储在低地址上,
低位存储在高地址上,简称“高低低高”。LSB FIRST则相反,即低位优先存储,高位存储在高地址上,低位存储在低地址上,简称“高高低低”。大部分嵌入式系统的CPU是MSB FIRST的,少部分是LSB FIRST的。常见的LSB FIRST的CPU是INTEL的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。