不同的平台(操作系统、编译器)上对类型的定义不尽相同,要注意其中的精确定义,以免踩坑。
【例】一个常见的例子就是:32位机器上的int类型占4个字节,而64位CPU上的int类型占8个字节。
【例】另一个常见的例子就是:32位机器上的指针类型占4个字节,而64位CPU上的指针类型占8个字节。
【例】还有一个比较极端的例子,曾经在嵌入式平台上使用linux-arm-gcc进行开发时,发现该编译器对char默认的定义是unsigned的。 好可怕,给char类型变量赋值一个负数,会得到错误的结果,而且编译器不会报错或者警告 幸好恰巧使用了“==”进行判别,编译器提出了警告,才得以提前发现这个问题。
【解法】有一种方法可以从源头上避免踩坑,那就是使用stdint.h头文件中的精确类型定义。
stdint.h包含了“int8_”,“uint8_”,“int16_t”,“uin16_t”,“in32_t”,“uin32_t”,“int64_t”,“uint64_t”这几种类型,使用这种类型就完全没有歧义的,推荐使用。
浮点数转定点数不能直接截取,必须四舍五入,否则精度会丢失,有时候会丢失很多!
【例】例如将浮点数转换成16Q8的定点数,当输入0.035时,0.035*256.0=8.96,直接截取就变成了8,然而8对应着0.03125,9对应着0.03515625,很明显应该是9更合适。 我们自然而然地想到+0.5,例如第二行代码所示,但是这样仍然是有问题的,就是在使用负数的时候。 例如输入-0.6,那么照理说-0.6更接近-1,然而-0.6+0.5=-0.1会截取成0,没有错(int)(-0.1)=0,在VS和gcc上是这样的。
输出结果:
【解法】一种解法是做判断,感觉不是很优雅,每次做个计算都得if一下。
以纳秒为单位UTC时间数值上特别大,举个例子(946,685,275,874,349,312,这一连串是一个数字),大概是30年左右。
很多32位平台的程序喜欢使将指针赋值给整数,这样就不需要二级指针,例如
如果需要在函数里面开内存,输出内存地址,那么要么使用二级指针,要么用这种转地址的方式。 但是,这种方式在跨平台时存在风险,在64位系统中,指针的大小可能是64位的,这样赋值就会出错。 仍然想用这种方式的话,使用intptr_t类型,兼容32和64位,在C99中支持,在<stdint.h>中定义。
防止数组访问溢出 假设在函数func里操作一个size为N的数组,函数传入需要操作的index,那么在没有前置判断溢出的情况下,需要加入判断是否溢出的语句。
如果数组的size不大,而且具有明确的含义,例如某种过程处理通道的个数,可以考虑使用枚举来代替。 使用枚举的好处是,枚举变量一定不会超出定义域的范围,在编译阶段就决定了不会产生数组的越界。
即使变量长度没有问题,常量表达式也会溢出,如下代码所示。 想在想要使用纳秒来表达8个小时的时长,第一种计算方法是错误的,“8”,“3600“以及”1000000000“都是int32_t, 它们每个都在int32_t的表达范围内,但是乘起来就会超出。因为三个常量都是int32_t类型,所以编译器最终选择输出int32_t类型,最终常量溢出。 哪怕前面用了uin64_t的变量来接受赋值,但是右边的计算已经溢出了。
第二种方法是正确的,随便在其中一个数字后面加上LL,那么编译器最终会选择LL类型输出,也就是uint64_t,这样计算就不会溢出。
使用纳秒来表示从公元0年到公元2000年的时间。
需要表达的时间大致为:2000*365*24*3600*1e9 = 63,072,000,000,000,000,000
而uint64能表达的最大数值为:numeric_limits<uint64_t>::max() = 18,446,744,073,709,551,615
显然,uint64_t无法表达这个数。
所以在使用ns来表示时间的时候,一定要注意溢出,连uint64都不够用。