混了嵌入式这么久,你了解过天天用的编译器吗?

editor Chen的头像
editor Chen 发布于:周五, 11/18/2016 - 17:53 ,关键词:

好多人说编译器只是工具,重要的在于算法和思想。

这话说的本来没错,但要有一个条件在先:那就是你真正掌握了你所用的编译器。但,真正熟悉编译器的却并不多见。当你深入了解一个编译器后,你能像用汇编一样用C,可以像汇编那样随心所欲的操作MCU!

了解一个编译器,首先应该有汇编的基础,不要求能用汇编编写程序或做过项目,但至少看的懂!不熟悉汇编的嵌入式程序员是不合格的程序员!

了解一个编译器,最好的方法是看它自带的帮助文件,至少要看过Compiler User's Guide ,至少遇到问题会想到到帮助中查找方法,虽然帮助大多是E文。

工作以来一直使用keil MDK编译器,对于这个编译器的界面以及设置大家可以在网上搜一下就找到了,今天我们主要来看一看keil MDK编译器的一些细节,看看这些细节,你知道多少。

1. 在所有的内部和外部标识符中,大写和小写字符不同。

2. 默认情况下,char 类型的数据项是无符号的。它们可以显式地声明为signed char 或 unsigned char。

3.基本数据类型的大小和对齐:

注:

a. 通常局部变量保留在寄存器中,但当局部变量太多放到栈里的时候,它们总是字对齐的。例如局部char变量在栈里以4为边界对齐;

b. 压缩类型的自然对齐方式为1。使用关键字__packed来压缩特定结构,将所有有效类型的对齐边界设置为1.

4. 整数以二进制补码形式表示;浮点量按IEEE格式存储。

5. 有符号量的右移是算术移位,即移位时要保证符号位不改变。

6. 对于int类的值:超过31位的左移结果为零;无符号值或正的有符号值超过31位的右移结果为零。负的有符号值移位结果为-1。

7. 整数除法的余数的符号于被除数相同,由ISO C90标准得出;

8. 如果整型值被截断为短的有符号整型,则通过放弃适当数目的最高有效位来得到结果。如果原始数是太大的正或负数,对于新的类型 ,无法保证结果的符号将于原始数相同。所以强制类型转化的时候,对转换的结果一定要清晰。

9. 整型数超界不引发异常;像unsigned char test; test=1000;这类是不会报错的,赋值或计算时务必小心。

10. 默认情况下,整型数除以零返回零。

11. 对于两个指向相同类型和对齐属性的指针相减,计算结果如下表达式所示:
((int)a (int)b) / (int)sizeof(指向数据的类型)

12. 在严格C中,枚举值必须被表示为整型,例如,必须在2147483648 到+2147483647的范围内。但keil MDK自动使用对象包含enum范围的最小整型来实现(比如char类型),除非使用编译器命令enum_is_int 来强制将enum的基础类型设为至少和整型一样宽。超出范围的枚举值默认仅产生警告:#66: enumeration value is out of "int" range

13. 结构体:
struct {
char c;
short s;
int x;
} //这个结构体占8个字节

但是,结构体:
struct {
char c;
int x;
short s;
} //这个结构体占12个字节

这是为什么?

对于结构体填充,据定义结构的方式,keil MDK编译器用以下方式的一种来填充结构:
定义为static或者extern的结构用零填充;

栈或堆上的结构,例如,用 malloc() 或者 auto定义的结构,使用先前存储在那些存储器位置的任何内容进行填充。不能使用memcmp()来比较以这种方式定义的填充结构!

14. 编译器不对声明为volatile 类型的数据进行优化。 我发现还有不少刚入门的嵌入式程序员从没见过这个关键字.

15. __nop():延时一个指令周期,编译器绝不会优化它。如果硬件支持NOP指令,则该句被替换为NOP指令,如果硬件不支持NOP指令,编译器将它替换为一个等效于NOP的指令,具体指令由编译器自己决定。

围观 575