嵌入式面试常考问题

面试经验

c/c++

1、malloc与new的区别

1、new是操作符,malloc是函数

2、new使用时先分配内存,再调用构造函数,释放时调用析构函数

3、new只能分配实例所占类型的整数倍,malloc可以随意分配。

4、new失败返回异常,malloc返回NULL

2、C语言内存分配的方式

1、静态区分配:编译时分配好,主要储存全局变量,static变量等。

2、栈分配:执行函数时,函数内部的局部变量,函数结束时,这些储存单元自动被释放

3、堆分配:也叫动态分配,通过malloc以及NEW来分配,程序员自己负责free或者delete自己申请的内存

3、struct 与 class的区别

1、class可以继承,类,接口,struct只能继承接口

2、class有默认的无参构造函数,struct没有,而且struct没有析构函数

3、class有继承级别,protected等等

4、class用垃圾回收机制保证内存的回收,struct使用完之后自动接触内存分配。

4、const常量和#define的区别

1、前者在编译期起作用,后者在预处理和编译期起作用

2、前者没有数据类型,只是简单的替换,后者有数据类型,可以进行判断,避免基础的错误。

3、前者只有一个备份,后者替换多少次就有多少个备份。

5、vector与list

1、vector 内存空间连续,底层是数组,list内存空间不连续,是双向链表,都是在堆中分配

2、vector随机访问效率高,在非尾部插入困难,list相反

3、迭代器支持不同,vector支持的“+”等等,list不支持

6、各个stl的底层实现

1、vector 为数组,支持快速随机访问

2、list底层为 双向链表,支持快速增删

3、deque是中央控制器和多个缓冲区

4、stack底层一般用list和deque实现

5、queue一般使用list和deque实现,封闭头部

6、priority_queue使用vector为底层容器,使用heap来管理规则

7、set底层为红黑树,有序不重复 multiset可重复

8、map底层为红黑树,有序不重复 multimap可重复

9、hash_set底层为hash表,无序不重复

10、hash_map底层为hash表 无序不重复

7、动态绑定与静态绑定

1、动态绑定就是继承虚函数

2、静态绑定就是函数重载

8、多态实现的三个条件、实现的原理

条件:有继承、有虚函数(virtual)重写、有父类指针(引用)指向子类对象。

实现原理:当类中声明虚函数时,编译器会在类中生成一个 虚函数表;虚函数表是一个储存 类成员函数指针的数据结构;virtual成员函数会被编译器放入虚函数表中。存在虚函数时,在创建的每个对象中都有一个指向虚函数表的指针(vptr指针)函数在运行的时候会重写这个虚函数。

9、析构函数一般写成虚函数的原因

由于类的多态性,基类指针可以指向派生类的对象。如果删除该基类的指针,就会调用该指针指向的派生类的析构函数,而派生类的析构函数又会自动调用基类的析构函数,这样整个派生类的对象被完全释放

10、构造函数不能是虚函数的原因

虚函数相应一个指向vtable虚函数表的指针,但是这个指向vtable的指针事实上是存储在对象的内存空间的。假设构造函数是虚的,就须要通过 vtable来调用,但是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。

11、抽象类与纯虚函数
  • 纯虚函数: 一个函数只有函数名和形参列表,没有具体实现;语法:virtual double GetArea()=0;

  • 抽象类: 在C++中,含有纯虚拟函数的类称为抽象类,它不能生成对象。抽象类是不完整的,它只能用作基类。

12、重载、覆盖

重载是参数类型或者个数不同,覆盖是子类重写父类函数。

13、栈和队列

1、规则:栈先入后出,队列先入先出

2、插入删除定义不同:栈只能在一端插入和删除,队列只能在一端插入另一端删除

14、strcpy和memcpy的区别

(1)复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

(2)复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

(3)用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。

15、了解智能指针吗?

主要4个,auto_ptr(C++ 98)、unique_ptr、shared_ptr 和weak_ptr(C++ 11)

auto_ptr :现在用的少,老版C++用,能自动释放。

unique_ptr: 两个unique_ptr 不能指向一个对象,即 unique_ptr 不共享它所管理的对象,并可以放在容器中。

shared_ptr:shared_ptr 是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,shared_ptr 利用引用计数的方式实现了对所管理的对象的所有权的分享,即允许多个 shared_ptr 共同管理同一个对象。比较麻烦,需要自己写辅助类。

weak_ptr: weak_ptr 是为了配合 shared_ptr 而引入的一种智能指针,它更像是 shared_ptr 的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载 operator* 和 operator-> ,因此取名为 weak,表明其是功能较弱的智能指针。

16、lambda函数

模板:capture mutable ->return-type{statement}

1.[capture]:捕捉列表。捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;

2.(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;

3.mutable:mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空);

4.->return-type:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;

5.{statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。语法上,在“[]”包括起来的是捕捉列表,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:

1.[var]表示值传递方式捕捉变量var;
2.[=]表示值传递方式捕捉所有父作用域的变量(包括this);
3.[&var]表示引用传递捕捉变量var;
4.[&]表示引用传递方式捕捉所有父作用域的变量(包括this);
5.[this]表示值传递方式捕捉当前的this指针。

上面提到了一个父作用域,也就是包含Lambda函数的语句块,说通俗点就是包含Lambda的“{}”代码块。上面的捕捉列表还可以进行组合,例如:

1.[=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
2.[&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量。

不过值得注意的是,捕捉列表不允许变量重复传递。下面一些例子就是典型的重复,会导致编译时期的错误。例如:

3.[=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;
4.[&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。

17、说说C++的模板
18、未初始化的全局变量和局部变量值是什么?

全局变量为0,局部变量栈上分配为随机数。

局部变量为随机数的主要原因是因为局部变量是栈上分配的,栈内存是反复使用的,如果不进行初始化就是上一次写入的值。

19、说说友元类,是否是相互的,是否能被继承

使用友元类时注意:

​ (1) 友元关系不能被继承。

​ (2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。

​ (3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

20、说说你用的C++版本,C++11有什么新特性?

1、auto变量类型自动推导

2、基于范围的for循环 for(int a:vec)

3、智能指针

4、stl 新增unordered 哈希表map

5、lambda函数:无名函数,用在sort中比较多

6、线程库

21、如何防止Double Free

先判断一波

if§

{

free§;

}

或者每次free之后把指针指向NULL;

22、(IO多路复用)select、poll、epoll区别和实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fDi7ZUeD-1669710834043)(https://vipkshttps15.wiz.cn/editor/82363800-126d-11ed-92fe-017f9d161f3f/11473e42-1881-475e-85cf-79bc99be3c2c/resources/EHIUFSNFwA9sBM4qqITFkhp0TumlIX7TkpG5Wn_p18E.png?token=W.no8iG4jpkwK46lJ5WxRJFF3UqKkka8sbsTM-36WRMKzM4sYCR5IxYQXmlm_YStU)]

实现见 socket IO复用

select:

优点:所有平台都支持,良好的跨平台支持也是一个优点

缺点:

1、数量限制,Linux平台一般是1024个

2、线性扫描,采用轮询的方法,效率较低

3、select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理,因此需要维护一个用来存放大量fd的数据结构(fd_set)fd_set简单地理解为一个长度是1024的比特位,每个比特位表示一个需要处理的FD,如果是1,那么表示这个FD有需要处理的I/O事件,否则没有,其是连续存储的。每次select查询都要遍历整个事件列表。

poll:

主要操作的数据结构:

struct pollfd {

int fd; // 需要监视的文件描述符

short events; // 需要内核监视的事件

short revents; // 实际发生的事件

};

优点:

1、在使用该结构的时候,不用进行比特位的操作,而是对事件本身进行操作就行。同时还可以自定义事件的类型。这样的好处是在内存中存放就不需要连续的内存地址,很像是list队列结构,读或者写事件数量(文件描述符数量)理论上是无限的,取决于内存的大小。

2、它没有最大连接数的限制,原因是它是基于链表来存储的。

缺点:

  • 内核需要将消息传递到用户空间,都需要内核拷贝动作。需要维护一个用来存放大量fd的数据结构,使得用户空间和内核空间在传递该结构时复制开销大。大量的fd被整体复制于用户态和内核地址空间之间,而
  • 6
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值