嵌入式八股文汇总
Miss shirly 2024-06-10 13:35:04 阅读 54
1、C/C++
1.1 关键字
(参考”嵌入式及Linux那些事“以及众多帖子汇总而成)
volatile
当声明指向设备寄存器的指针时一定要用volatile,它会告诉编译器不要对存储在这个地
址的数据进行假设。
中断服务程序中修改的供其他程序检测的变量。 中断中直接从变量地址中读取数据,而不是从寄存器中读取。
多线程应用中被几个任务共享的变量。
static
1、函数体内的变量,这个变量只被声明一次。
2、在模块内的变量,表示只能被模块内函数使用,不能被模块外函数访问,表示本地全局变量
3、模块内的函数,限制在模块内使用,同上。
extern
1、引用同一文件变量
使用在声明之前时,可以使用关键字extern,让声明在程序任意位置。
2、引用另一个文件中的变量
extern可以引用其他文件中的全局变量,而且extern只需要指明数据类型和
extern int num=4; 这样不行。
3、引用另一个文件中的函数
可以不用包含头文件引用函数。
new/delete malloc/free
1、new/delete是操作符,malloc/free是库函数
2、new/delete可以调用构造函数/析构函数,m/f 只是分配内存。
struct 和 union区别
1、联合体公用一块地址空间,联合体变量长度等于最长的成员的长度
2、对不同成员赋值,会将其他成员重写。
const
1、定义变量为常量
2、修饰参数为常量
3、修饰返回值为常量
总结:只读
sizeof和strlen
1、sizeof是运算符,strlen是函数
2、sizeof可以用类型、函数作为参数,strlen只能计算char*,还必须以/0结尾
3、sizeof编译的时候计算,strlen是运行期计算,表示字符串长度,不是内存大小。
typedef和 define
1、都是替对象去一个别名,增强程序的可读性
2、define为预处理指令,不做正确性检查,只有带入之后才能发现
3、typedef用来定义类型别名,不止包含内部类型还包含自定义类型(与机器无关),方便记忆
4、define不仅可以给类型取别名,还能定义常量、变量、编译开关。
5、define没有作用域限制,typedef有。
# define还是 const ,谁定义常量最好
1、define只是文本替换,声明周期止于编译期,不分配内存空间,存在于代码段。const常量存在于数据段,堆栈中分配了空间。
2、const有数据类型,编译器可以对const进行安全检查。
3、const有保护常量不被修改的作用,提高程序的健壮性。
总结:一般倾向于用const定义常量
1.2 内存
C语言内存分配方式
1、静态储存区分配
2、栈上分配
3、堆上分配
C++内存管理是怎样的
分为代码段、数据段、BSS段、堆区、栈区、文件映射区
代码段:分为只读区和文本区,只读取储存字符串常量,文本区储存机器代码。
数据段:储存以及初始化的全局变量和静态变量
BSS段:储存未初始化的全局变量和静态变量,以及初始化为0的全局和静态。
堆区:手动分配的内存
栈:局部变量参数返回值等
映射区:储存动态链接库,mmap函数的文件映射
堆和栈的区别
1、申请方式。 栈为操作系统自动分配/释放,堆为手动
2、申请大小,栈空间有限,向低地址拓展的连续区域,堆是向高地址拓展的不连续区域,链表储存的空闲地址。
3、申请效率,栈是系统自动分配,速度快,不可控。堆是由new分配,速度比较慢,容易产生内存碎片。
栈的作用
1、储存临时变量
2、多线程编程的基础。每个线程至少有一个栈用来存储临时变量和返回的值。
内存泄漏
申请了没有释放,由程序申请的一块内存,没有任何指针指向它,这个内存就泄露了。
避免内存泄漏方法
1、分配的内存以链表管理,使用完毕后从链表删除,程序结束的时候检查链表
2、良好的编程习惯,在设计内存的程序段,检验出内存泄漏,使用了内存分配的函数,使用完毕后将使用的相应函数释放掉
3、smart pointer
指针
数组指针和指针数组
int (*p)[20]; 数组指针,本质是一个指针,指向一个数组
int *p[20]; 指针数组,本质是一个数组,里面装的是指针。
函数指针和指针函数
1、函数指针 int(*p)(int,int);本身是一个指针,指向一个函数的地址
2、指针函数 int *p(int,int); 指针函数表示一个函数,返回数是指针。
数组名和指针区别
1、指针保存的是地址,数组保存的是数据,单数组名是第一个元素的地址
2、指针间接访问,数组直接下标或者偏移量
3、sizeof 有区别,指针为指针大小,数组为全体数据大小
指针常量,常量指针、指向常量的指针
1、int *const p 指向地址不变,地址值可变
2 int const *p 指向地址可变,地址值不能边
3、const int * const p 都不能变
指针与引用区别
1、都是地址,指针是地址,应用是别名
2、引用本质是指针常量,对象不变,对象的值可变
3、++不同,指针是地址自增,引用是对象自增
4、指针需要解引用
5、指针可为空,引用不行
6、sizeof不同 一个是指针大小一个是对象大小
野指针
1、指向不可用内存的指针,指针被创建时如果没有初始化就是野指针
2、指针被free、delete时没有指向NULL就是野指针
3、指针超出了变量的地址范围
智能指针
C++智能指针是指一个类,用来存储指针
1.3 预处理
预处理器标识#error的目的是什么?
1、遇到#error就会生成一个编译错误提示信息,并停止编译
define声明一年多少秒
#define SECOND_OF_PER_YEAR (3652460*60)UL
#include"" 和 include<>区别
<>号先搜索标准库搜索系统文件比较快,“”号先搜索工作路径搜索自定义文件比较快
头文件作用
1、通过文件调用库功能,源码保护
2、头文件加强类型安全检查,编译器报错
头文件定义静态变量
1、资源浪费,每个头文件都会单独存在一个静态变量
不使用流程控制语句,打印1~1000数字
#include<stdio.h>#define A(x) x;x;x;x;x;x;x;x;x;x;int main(){int n=1;A(A(A(printf("%d",n++))));return 0;}
1.4 变量
全局变量和静态变量
1、全局变量作用域为程序块,局部变量为当前函数
2、全局变量储存在静态区,后者为栈
3、全局变量生命周期为主函数,局部变量生命周期在局部函数中,甚至循环体内
1.5 函数
写个函数在main函数执行前执行
1、attribute可以设置函数属性
#include <stdio.h>void before() __attribute__((constructor));void after() __attribute__((destructor));void before() { printf("this is function %s\n",__func__); return;}void after(){ printf("this is function %s\n",__func__); return;}int main(){ printf("this is function %s\n",__func__); return 0;}// 输出结果// this is function before// this is function main// this is function after
为什么析构函数必须是虚函数
1、基类指针指向子类时,释放基类指针也能释放掉子类的空间,防止内存泄漏。
2、最好是作为父类的类的析构函数作为虚函数
为什么C++默认的析构函数不是虚函数?
1、虚函数有额外的虚函数表和虚指针表,占用额外的内存,对于那些不会被继承的类当然也不需要虚函数作为析构函数。
静态函数和虚函数的区别?
1、静态函数编译时确定运行时机
2、虚函数运行时动态绑定,并且使用虚函数表,内存开销增加
重载与覆盖
1、覆盖是子类和父类的关系,垂直关系,重载是一个类之间的关系,水平关系
2、覆盖一对一,重载多个方法
3、覆盖由对象类型决定,重载根据调用的参数表决定
虚函数表实现多态方法
原理:
虚函数表示一个类的地址表,子类创建时,按照函数声明吮吸会将函数的地址存在虚函数表中。子类重写父类虚函数的时候,父类虚函数表中的位置会被子类虚函数地址覆盖。
C语言函数调用方法
1、使用栈来支持函数调用操作,栈被用来传递参数,返回值,局部变量等。
2、函数调用主要操作栈帧结构
select函数
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
fork wait exec函数
1、附近产生的子进程使用fork拷贝出一个父进程的副本
数组的下标可以为负数吗?
可以,数组下标指地址偏移量,根据偏移量能定位得到目标地址。
inline函数和宏定义的区别
1、内联函数在编译时展开,而宏在预编译时展开。
2、在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。
3、内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能。
4、宏不是函数,而inline是函数。
5、宏在定义时要小心处理宏参数,一般用括号括起来,否则容易出现二义性。而内联函数不会出现二义性。
6、inline可以不展开,宏一定要展开。因为inline指示对编译器来说,只是一个建议,编译器可以选择忽略该建议,不对该函数进行展开。
7、宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做预处理器符号表中的简单替换,因此它不能进行参数有效性的检测,也就不能享受C++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。
宏和函数的优缺点
1、函数调用时,先求出实参表达式的值,然后带入形参。而使用带参数的函数只是进行简单的字符替换
2、函数调用实在程序运行时处理的,分配的临时的内存单元;而宏展开则是在编译时进行的,在展开时不分配i内存单元,不进行值的传递,也没有"返回值的概念"
3、函数实参形参都要定义类型,二者要求一致 ,宏不存在类型问题,宏没有类型,宏的参数只是一个符号代表,展开时代入指定的字符就行,宏定义时字串可以是任意内心的数据
4、函数只可以得到一个返回值,宏可以设法得到多个
5、使用宏次数多时,展开后源程序长,每次展开都使程序增长,而函数调用不使源程序变长。
6
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。