嵌入式八股文汇总

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。