1、编译原理:编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。
链接:是把目标文件,操作系统的启动代码和用到的库文件进行组织,最终形成可执行代码的过程。
编译过程分为两个阶段:编译(产生.S文件,包含预编译)+汇编(产生.O文件)。
编译是读取源代码(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码。
(1)、宏定义指令:如#define a b; #undef ;属于伪指令。
(2)、条件编译指令:如#ifdef, #ifndef ,#else , #elif , #endif ;属于伪指令,将那些不必要的代码过滤掉
(3)、头文件包含指令:如#include<>或#include " " 等。在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。
在程序中#include他们要使用尖括号<> ,另外开发人员也可以定义自己的头文件 ,将这些文件一般与C源程序放在同一目录下,此时在#include中要用双引号 " " 。
(4)、特殊符号:预编译程序可以识别一些特殊的符号。例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。
预编译程序所完成的基本上是对源程序的“替代”工作,经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。
2、关键字static、const、volatile
在C语言中,关键字static有三个明显的作用:
(1)、在函数体内,一个被声明为静态的变量,在这一个函数被调用过程中维持其值不变
(2)、在模块内(但在函数体外),一个被声明为静态的变量,可以被模块内所用函数访问,但不能被模块外其他函数访问,他是一个本地的全局变量。
(3)、在模块内,一个被声明为静态的函数只能被这一模块内的其他函数调用。那就是这个函数被限制在声明他的模块的本地范围内使用。
const变量:const是constant的缩写,”恒定不变“的意思。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
const常量与#define常量:前者有数据类型,后者只是字符替换(而且可能产生意料不到的错误),所以编译器可以对前者进行安全性检查。
一般来说,volatile用在如下的几个地方:
1、中断服务程序中修改的供其他程序检测的变量需要加volatile
2、多任务环境下各任务间共享的标志应该加volatile
3、存储器映射的硬件寄存器通常也要加volatile说明,因此每次对他的读写都可能有不同意义
volatile关键字是会降低编译器优化力度的,但他保证了程序的正确性,所以在适合的地方使用volatile是件考验编程功底的事情。
不可重入函数:不可重入函数不可以在它还没有返回就再次被调用。例如printf, malloc, free 等都是不可重入函数。因为中断可能在任何时候发生,例如在printf执行过程中;因此不能在中断处理函数里调用printf,否则printf将会被重入。
函数不可重入大多数是因为在函数中引用了全局变量。例如,printf会引用全局变量stdout,malloc,free会引用全局变量的内存分配表。
不可重入函数指的是该函数在被调用还没有结束以前,再次被调用可能会产生错误。可重入函数不存在这样的问题。不可重入函数在实现时候通常使用了全局的资源,在多线程的环境下,如果没有很好的处理数据保护和互斥访问,就会发生错误。
只要遵循了几条很容易理解的规则,那么写出来的函数就是可重入的。
第一、不要使用全局变量。因为别的代码很可能覆盖这些变量值。
第二、在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL来描述。
第三、不能调用任何不可重入的函数
第四、谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL。
还有一些规则,都是很好理解的,总之,时刻记住一句话:保证中断是安全的。