嵌入式软件工程师面试题——2025校招社招通用(C/C++)(四十五)

说明:

  • 面试群,群号: 228447240
  • 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);
  • 文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要经过认真思考,答案不重要,重要的是通过题目理解所考知识点,好应对题目更多的变化;
  • 博主与大家一起学习,一起刷题,共同进步;
  • 写文不易,麻烦给个三连!!!

前面1-15已经是C/C++,但是由于前面写的比较混乱,把八股文和题目混在了一起,所以从这一篇开始重新整理重新写,前面1-15也就可以选看了,希望多多支持!

目录

1.C++中struct和class的区别

2.数组名和指针(这里为指向数组首元素的指针)区别?

3.final和override关键字

4.拷贝初始化和直接初始化

5.野指针和悬空指针

6.C++中的重载、重写(覆盖)和隐藏的区别

7.mutable和explicit关键字的用法

8.什么情况下会调用拷贝构造函数

9.形参与实参的区别?

10.值传递、指针传递、引用传递的区别和效率

11.delete p、delete [] p、allocator都有什么作用?

12.malloc申请的存储空间能用delete释放吗?

1.C++structclass的区别

答案:

相同点
两者都拥有成员函数、公有和私有部分。
任何可以使用class完成的工作,同样可以使用struct完成。
不同点
两者中如果不对成员不指定公私有,struct默认是公有的,class则默认是私有的。
class默认是private继承,而struct模式是public继承。
引申:C++和C的struct区别
1. C语言中:struct是用户自定义数据类型(UDT);C++中struct是抽象数据类型(ADT),支持成员函数的定义,(C++中的struct能继承,能实现多态)
2. C中struct是没有权限的设置的,且struct中只能是一些变量的集合体,可以封装数据却不可以隐藏数据,而且成员 不可以是函数
3. C++中,struct增加了访问权限,且可以和类一样有成员函数,成员默认访问说明符为public(为了与C兼容)
4. struct作为类的一种特例是用来自定义数据结构的。一个结构标记声明后,在C中必须在结构标记前加上struct,才能做结构类型名(除:typedef struct class{};);C++中结构体标记(结构体名)可以直接作为结构体类型名使用,此外结构体struct在C++中被当作类的一种特例

2.数组名和指针(这里为指向数组首元素的指针)区别?

答案:
二者均可通过增减偏移量来访问数组中的元素。
数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。
当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作, sizeof 运算符不能再得到原数组的大小了。

3.finaloverride关键字

答案:
override
当在父类中使用了虚函数时候,你可能需要在某个子类中对这个虚函数进行重写,以下方法都可以:
class A
{
 virtual void foo();
}
class B : public A
{
 void foo(); //OK
 virtual void foo(); // OK
 void foo() override; //OK
}
如果不使用override,当你手一抖,将 foo()写成了 f00()会怎么样呢?结果是编译器并不会报错,因为它并不知道你的目的是重写虚函数,而是把它当成了新的函数。如果这个虚函数很重要的话,那就会对整个程序不利。所以,override的作用就出来了,它指定了子类的这个虚函数是重写的父类的,如果你名字不小心打错了的话,编译器是不会编译通过的。
final
当不希望某个类被继承,或不希望某个虚函数被重写,可以在类名和虚函数后添加final关键字,添加final关键字后被继承或重写,编译器会报错。
class Base
{
 virtual void foo();
};
class A : public Base
{
 void foo() final; // foo 被override并且是最后一个override,在其子类中不可以重写
};
class B final : A // 指明B是不可以被继承的
{
 void foo() override; // Error: 在A中已经被final了
};
class C : B // Error: B is final
{
};

4.拷贝初始化和直接初始化

答案:
当用于类类型对象时,初始化的拷贝形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数。拷贝初始化首先使用指定构造函数创建一个临时对象,然后用拷贝构造函数将那个临时对象拷贝到正在创建的对象。
string str1("I am a string");//语句1 直接初始化
string str2(str1);//语句2 直接初始化,str1是已经存在的对象,直接调用构造函数对str2进行初始
化
string str3 = "I am a string";//语句3 拷贝初始化,先为字符串”I am a string“创建临时对象,
再把临时对象作为参数,使用拷贝构造函数构造str3
string str4 = str1;//语句4 拷贝初始化,这里相当于隐式调用拷贝构造函数,而不是调用赋值运算符
函数

为了提高效率,允许编译器跳过创建临时对象这一步,直接调用构造函数构造要创建的对象,这样就完全等价于直接初始化了(语句1和语句3等价),但是需要辨别两种情况。

  • 当拷贝构造函数为private时:语句3和语句4在编译时会报错
  • 使用explicit修饰构造函数时:如果构造函数存在隐式转换,编译时会报错

5.野指针和悬空指针

答案:
都是是指向无效内存区域(这里的无效指的是"不安全不可控")的指针,访问行为将会导致未定义行为。
野指针: 指的是没有被初始化过的指针 。
因此,为了防止出错,对于指针初始化时都是赋值为 nullptr,这样在使用时编译器就会直接报错,
产生非法内存访问。
悬空指针: 指针最初指向的内存已经被释放了的一种指针。 
产生原因及解决办法:
野指针:指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。
悬空指针:指针free或delete之后没有及时置空 => 释放操作后立即置空。

6.C++中的重载、重写(覆盖)和隐藏的区别

答案:
(1)重载(overload)
重载是指在同一范围定义中的同名成员函数才存在重载关系。主要特点是函数名相同,参数类型和数目有所不同,不能出现参数个数和类型均相同,仅仅依靠返回值不同来区分的函数。重载和函数成员是否是虚函数无关。
(2)重写(覆盖)(override)
重写指的是在派生类中覆盖基类中的同名函数, 重写就是重写函数体要求基类函数必须是虚函数且:
   与基类的虚函数有相同的参数个数
   与基类的虚函数有相同的参数类型
   与基类的虚函数有相同的返回值类型
(3)隐藏(hide)
隐藏指的是某些情况下,派生类中的函数屏蔽了基类中的同名函数,包括以下情况:
   两个函数参数相同,但是基类函数不是虚函数。 和重写的区别在于基类函数是否是虚函数。
   两个函数参数不同,无论基类函数是不是虚函数,都会被隐藏。和重载的区别在于两个函数不在  同一 个类中。

7.mutableexplicit关键字的用法

答案:
mutable
mutable的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要 const 函数里面修改一些跟类状态无关的数据成员,那么这个函数就应该被 mutable 来修饰,并且放在函数后后面关键字位置
explicit
explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以 显示的方式进行类型转换,注意以下几点:
  • explicit 关键字只能用于类内部的构造函数声明上
  • explicit 关键字作用于单个参数的构造函数
  • 被explicit修饰的构造函数的类,不能发生相应的隐式类型转换

8.什么情况下会调用拷贝构造函数

答案:
  • 用类的一个实例化对象去初始化另一个对象的时候
  • 函数的参数是类的对象时(非引用传递)
  • 函数的返回值是函数体内局部对象的类的对象时 ,此时虽然发生(Named return Value优化)NRV优化,但是由于返回方式是值传递,所以会在返回值的地方调用拷贝构造函数

9.形参与实参的区别?

答案:
1) 形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量。
2) 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值,会产生一个临时变量。
3) 实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。
4) 函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
5) 当形参和实参不是指针类型时,在该函数运行时,形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变。

10.值传递、指针传递、引用传递的区别和效率

答案:
1) 值传递:有一个形参向函数所属的栈拷贝数据的过程,如果值传递的对象是类对象 或是大的结构体对象,将耗费一定的时间和空间。(传值)
2) 指针传递:同样有一个形参向函数所属的栈拷贝数据的过程,但拷贝的数据是一个固定为4字节的地址。(传值,传递的是地址值)
3) 引用传递:同样有上述的数据拷贝过程,但其是针对地址的,相当于为该数据所在的地址起了一个别名。(传地址)
4) 效率上讲,指针传递和引用传递比值传递效率高。一般主张使用引用传递,代码逻辑上更加紧凑、清晰。

11.delete pdelete [] pallocator都有什么作用?

答案:
1、 动态数组管理new一个数组时,[]中必须是一个整数,但是不一定是常量整数,普通数组必须是一个常量整数;
2、 new动态数组返回的并不是数组类型,而是一个元素类型的指针;
3、 delete[]时,数组中的元素按逆序的顺序进行销毁;
4、 new在内存分配上面有一些局限性,new的机制是将内存分配和对象构造组合在一起,同样的, delete也是将对象析构和内存释放组合在一起的。allocator将这两部分分开进行,allocator申请一部分内存,不进行初始化对象,只有当需要的时候才进行初始化操作。

12.malloc申请的存储空间能用delete释放吗?

答案:
不能,malloc /free主要为了兼容C,new和delete 完全可以取代malloc /free的。
malloc /free的操作对象都是必须明确大小的,而且不能用在动态类上。
new 和delete会自动进行类型检查和大小,malloc/free不能执行构造函数与析构函数,所以动态对象它是不行的。
当然从理论上说使用malloc申请的内存是可以通过delete释放的。不过一般不这样写的。而且也不能保证每个C++的运行时都能正常。

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值