底层软件开发小白(励志在底层领域有所建树,养家糊口同时望能为国家贡献一份力)记录工作过程中自己不懂的相关知识,一来可以加强自己的记忆提高自己,二来希望能给相关的底层开发同仁一点帮助。如有错误的地方帮忙指出,谢谢。
现在刚接触项目,只能遇到不会的地方记录下来,没有逻辑关系,等一段时间再统一整理。
1 static inline函数
头文件中常见static inline,用于定义函数
引入内联函数的目的是为了解决程序中函数调用的效率问题,内联函数用于函数体小,引用频繁的函数放到头文件中,节省调用函数时需要的保护现场和恢复现场,用空间换时间。
2. static修饰变量和函数
A.c源文件中
char a = ‘A’; // global variable
void msg()
{
printf(“Hellon”);
}
main.c源文件中
int main(void)
{
extern char a; // extern variable must be declared before use
printf(“%c “, a);
(void)msg();
return 0;
}
运行结果:A Hello
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问——访问方法(1)在一个头文件中说明,要使用这些函数的源文件要包含这个头文件(2)在其他使用这个全局函数的源文件中,用extern先声明这个函数再使用。
而对于内部函数应该在当前源文件中说明和定义。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用:
(1) 保持变量内容的持久,只初始化一次
(2)默认初始化为0,字符默认初始化为’ ’
局部变量按照存储形式来分,分为auto,static,register
首先从内存四区的角度去看,auto即为普通的局部变量,存储在栈上,当函数结束时,随之释放。
register为寄存器变量,存放在寄存器里面,调用速度快。
在C语言中register变量不能取地址,会 错。
而在c++中,对register做了增强,党C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。
static修饰局部变量时该变量是存放在静态存储区,生命周期是整个程序结束。
const
关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变。C语言中已有#define, 而const修饰符有以下的优点:
1、预编译指令只是对值进行简单的替换,不能进行类型检查
2、可以保护被修饰的东西,防止意外修改,增强程序的健壮性
3、编译器通常不为普通const常量分配存储空间,而是将它们保存在符 表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
修饰局部变量const放在类型的前后没区别,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了。
常量指针 const int * n; 不能通过这个指针改变变量的值。可以通过其他的引用来改变变量的值,指向的地址可以改变。
指针常量 int const * n;指向的地址不能改变。指向的变量的值可以改变。
全局变量
全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,因为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样防止不必要的人为修改,使用的方法与局部变量是相同的。
3 –Io 指的是 (为什么加下划线免命名冲突)
__I volatile const:输入口。既然是输入,那么寄存器的值就随时会外部修改,那就不能进行优化,每次都要重新从寄存器中读取。也不能写,即只读,不然就不是输入而是输出了。
__O volatile:输出口,也不能进行优化,不然你连续两次输出相同值,编译器认为没改变,就忽略了后面那一次输出,假如外部在两次输出中间修改了值,那就影响输出
__IO volatile:输入输出口,同上
volatile
不让编译器进行优化,即每次读取或者修改值的时候,都必须重新从内存或者寄存器中读取或者修改。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的
1. volatile变量可变允许除了程序之外的比如硬件来修改他的内容
2. 访问该数据任何时候都会直接访问该地址处内容,即通过cache提高访问速度的优化被取
1)一个参数既可以是const还可以是volatile吗释为什么。
2); 一个指针可以是volatile 吗释为什么。
3); 下面的函数有什么错误:
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
4. 和””
引用的是编译器的类库路径里面的头文件
” “引用的是你程序目录的相对路径中的头文件
如果使用” “,它是会先在你项目的当前目录查找是否有对应头文件;如果没有,它还是会在对应的库目录里面查找对应的头文件
5. 声明和定义
-
变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。
-
变量声明:用于向程序表明变量的类型和名字。
-
定义也是声明,extern声明不是定义
-
函数的声明和定义区别比较简单,带有{ }的就是定义,否则就是声明
-
一般情况下头文件中只放变量的声明,因为头文件要被其他文件包含(即#include),如果把定义放到头文件的话,就不能避免多次定义变量
-
如果声明有初始化式,就被当作定义,即使前面加了extern。只有当extern声明位于函数外部时,才可以被初始化
6. 临界区和挂起调度器
访问一个被多任务共享,或是被多任务和中断共享的资源时,需要采用“互斥”技术以保证数据在任何时候都保持一致性。这样做的目的是要确保任务从开始访问资源就具有排它性,直到这个资源又恢复到完整状态。
临界区是提供互斥功能的一种非常原始的实现方法。临界区的工作 仅仅是简单的把中断全部关掉,或者是关掉优先级在configMAX_SYSCALL_INTERRUPT_PRIORITY以下的中断-依赖具体使用的FreeRTOS移植。抢占式的上下文切换只可能在某中断中完成,因此调用taskENTER_CRITICAL()可以在中断关闭的时段一直保持持续运行状态直到退出临界区。
基本临界区是指宏taskENTER_CRITICAL()和taskEXIT_CRITICAL()之间的代码区间基本临界区是保护一段代码区间不被其他任务或中断打断。而由挂起调度器实现的临界区只能保护一段代码不被其他任务打断,并不能约束中断,因为在这种方式下,中断是使能的。调度器处于挂起状态时,不能调用FreeRTOS的API函数。
编译器 将源文件变成目标文件即计算机能看懂的二进制文件
链接器 将目标二进制文件和库二进制文件(操作系统提供开发库)链接起来生成一个可执行程序
从源程序(.h .c文件)到可执行文件(ELF)的全过程如下:
(1)预处理
(2)转为汇编代码
(3)转为目标代码
(4)链接成可执行文件
分工
编译器工作阶段:(1) (2) (3)
链接器工作阶段:(4)
8. 堆栈帧
堆栈帧的创建步骤如下所示:
1) 被传递的实际参数。如果有,则压入堆栈。
2) 当子程序被调用时,使该子程序的返回值压入堆栈。
3) 子程序开始执行时,EEP 被压入堆栈。
4) 设置 EBP 等于 ESP。从这时开始,EBP 就变成了该子程序所有参数的引用基址。
5) 如果有局部变量,修改 ESP 以便在堆栈中为这些变量预留空间。
6) 如果需要保存寄存器,就将它们压入堆栈
9.
lib文件里就是放的我们平时中用的.c和.h文件
首先将C语言源文件经过C编译器生成相应的后缀为.o的目标文件,将汇编源文件(启动文件.s)也编译成相应的.o的目标文件, 最后通过连接器将各目标文件及存储器布局设置(option for target菜单设置)连接起来,生成后缀为.axf的可执行映像文件,这个映像文件可转化为二进制的程序映像文件.bin,也可以转换为十六进制文件.hex。
平时我们下载到芯片Flash中的代码就是.hex文件,上电后,内核将Flash中的代码加载到SRAM中,就可以开始执行代码了。而调试过程中是将.axf文件直接加载到芯片的SRAM中可以直接运行我们保存在.axf上的代码了。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!