目录
????????本篇内容主要列举一些自己在嵌入式开发过程中踩过的大坑(还有平时收集的一些易错点),希望能对遇到类似问题的开发者有所启发。
? ? ? ? 第一次碰到这个问题是在刚毕业面试那会,当时有一个面试题:怎么判断一个float类型数据是否为0?一开始看到这个问题的时候还真没当回事,不就 return (f == 0);就完事了吗?结果可想而知。现在回过头来看,确实这个回答略显业余。
void main()
{
float x =1.2; float y = 3.2;
if((x + y)==4.4) { printf(“correct\n”); }
else { printf(“ error \n”); }
}
结果是error!!!浮点数是为了能用二进制表达小数而引入的一种量化形式,转换时存在舍入差,因此不能直接用“==”比较浮点数,正确的做法应该是确定一个误差范围,通过比较两个浮点数的差值是否在这个误差范围内来判断是否相等。即
const float delta = 0.1
void main()
{
float x =1.2; float y = 3.2;
float z = x + y;
if((z < (4.4+delta))&&(z > (4.4-delta))) { printf(“correct\n”); }
else { printf(“ error \n”); }
}
那么很显然判断一个float数据是否等于0应该是如下表达
int isZero(float data)
{
//float类型一般精确到小数点后5位或6位
return(data > -0.000001 && data < 0.000001);
}
如下列代码所示
/*判断一*/
if(strlen(x)>= strlen(y))
{
}
/*判断二*/
if(strlen(x)- strlen(y)>= 0)
{
}
????????从表面上看,上面的两个判断表达式“完全相等”,但实际情况并非如此。其中,判断表达式一没什么问题,程序也能够完全按照预想的那样工作; 但判断表达式二的结果就不一样了,它将永远是真,这是为什么呢? 原因很简单,因为函数 strlen 的返回结果是 size_t 类型(即无符号整型),而 size_t 类型绝不可能是负的。因此,语句“if(strlen(x)-strlen(y)>=0)”将永远为真。
? ? ? ? 这个问题是在几天前碰到的,类似代码如下
unsigned char foo(void)
{
unsigned int a = 6;
int b = -20;
return (a+b > 6);
}
以上函数返回值为真!原因:当表达式中同时存在有符号类型和无符号类型时,所有的操作数都自动转换为无符号类型,上式中-20会被自动转换为一个非常大的正整数,所以结果会远远大于6。(如果这个问题出现在上大学那会,我想肯定不会出错,其实很多问题的解决方法往往是最基础的理论知识,像运算符优先级、自动转换这些概念往往容易出现问题,但却都是最基础的代码规则,所以大家在写代码时切勿眼高手低,遇到拿不准的地方及时巩固)
? ? ? ? 还是一个面试,当时面试官问我:int类型数据占用几个字节?4个字节!接着他问我确定吗?我犹豫了,因为我觉得他不会问这么简单的问题!那是不是就要分情况分析?不同编译器、不同位数的处理器是不是就不一样?我们常用的处理器都是32位,int类型默认占用4个字节,那如果是64位处理器是不是就是8个字节?float类型是不是也会有变化?究其原因还是对底层概念的模糊。
? ? ? ? 那么今天在这里做一个简单的归纳:数据类型的长度与编译器规则密不可分,尽管同样的类型在不同位数的系统上所代表的位数也可能不同,但是总归是有一些规则约束的。编译器可以根据自身硬件来选择合适的大小,但是需要满足约束:short和int型至少为16位,long型至少为32位,并且short型长度不能超过int型,而int型不能超过long型。这即是说各个类型的变量长度是由编译器来决定的,下面列举在GCC编译器下16位机器、32位机器和64位机器各个类型变量所占字节数:
C类型 | 16 | 32 | 64 |
---|---|---|---|
char | 1 | 1 | 1 |
short int | 2 | 2 | 2 |
int | 2 | 4 | 4 |
long long | 8 | 8 | 8 |
char* | 2 | 4 | 8 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
? ? ? ? 字长涉及到的易错点有很多,数据的字节对齐、结构体数据的步进值、指针移位等等,但这些相信大家平时接触的都比较多,这里不再详细列举,请看下列示例
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
如上代码,在一个int类型不是16位的处理器中表达就是不正确的,应该表示为
unsigned int zero = 0;
unsigned int compzero = ~0;
/*1's complement of zero */
(在单片机中,常采用“一补”(1's complement)格式,也即在16位处理器中,0xffff就是0,在32位单片机中,0xffffffff就是0;在手机端或者电脑端一般采用“二补”(2's complement),即0xffffffff就是-1)
value % x 等同于 value & (x-1),仅限于对偶数取余
????????使用场景:假如我们有很多种动物(猫,狗,鸡等等),都需想用一个结构体来定义,再假如结构体都只想描述动物的重量和身高,但又不想所有动物的结构体名称一致。假如结构体都想用Animal_打头,如Animl_Cat,同样我们用宏定义来实现:
#define StructureType(type) typedef struct{ \
float weight; \
float height; \
}Animal_ ## type;
使用:
StructureType(Cat);// 定义一个Animl_Cat结构体
Animal_Cat cat;// 实例化一个Animl_Cat
cat.weight = 10;
cat.height = 30;
你可能在想,我想定义一个新的动物结构体,增加一个动物属性,那就结合上面的组合方式
#define StructureTypeEx(type, member) typedef struct { \
float weight; \
float height; \
char* ## member; \
}Animal_ ## type;
使用:
StructureTypeEx(Dog, Name);// 定义了一个动物名
Animal_Dog dog;
dog.height = 60;
dog.weight = 100;
dog.Name = (char *)malloc(32);
memset(dog.Name, 0, 32);
strcpy(dog.Name, 7, "HuaHua");