二十万字C/C++、嵌入式软开面试题全集宝典七

目录

121、 怎样判断两个浮点数是否相等?

122、 宏定义一个取两个数中较大值的功能

123、 define、const、typedef、inline使用方法?

124、 printf实现原理?

125、 #include 的顺序以及尖括号和双引号的区别

126、 lambda函数

127、 模板类和模板函数的区别是什么?

128、 为什么模板类一般都是放在一个h文件中

129、 C++中类成员的访问权限和继承权限问题。

130、 cout和printf有什么区别?

131、 重载运算符?

132、 函数重载函数匹配原则

133、 定义和声明的区别

134、 全局变量和static变量的区别

135、 static函数与普通函数有什么区别?

136、 静态成员与普通成员的区别

137、 说一下理解 ifdef endif

138、 隐式转换,如何消除隐式转换?

139、 虚函数的内存结构,那菱形继承的虚函数内存结构呢

140、 多继承的优缺点,作为一个开发者怎么看待多继承


 

121、 怎样判断两个浮点数是否相等?

对两个浮点数判断大小和是否相等不能直接用==来判断,会出错!明明相等的两个数比较反而是不相等!对于两个浮点数比较只能通过相减并与预先设定的精度比较,记得要取绝对值!浮点数与0的比较也应该注意。与浮点数的表示方式有关。
const float EPSINON = 0.00001;
if (((a-b) >= - EPSINON) && ((a-b) <= EPSINON);

122、 宏定义一个取两个数中较大值的功能

#define MAX(x,y)((x>y?)x:y)

123、 define、const、typedef、inline使用方法?

一、const与#define的区别:
1.const定义的常量是变量带类型,而#define定义的只是个常数不带类型;
2.define只在预处理阶段起作用,简单的文本替换,而const在编译、链接过程中起作用;
3.define只是简单的字符串替换没有类型检查。而const是有数据类型的,是要进行判断的,可以避免一些低级错误;
4.define预处理后,占用代码段空间,const占用数据段空间;
5.const不能重定义,而define可以通过#undef取消某个符号的定义,进行重定义;
6.define独特功能,比如可以用来防止文件重复引用。
二、#define和别名typedef的区别
1.执行时间不同,typedef在编译阶段有效,typedef有类型检查的功能;#define是宏定义,发生在预处理阶段,不进行类型检查;
2.功能差异,typedef用来定义类型的别名,定义与平台无关的数据类型,与struct的结合使用等。#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
3.作用域不同,#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef有自己的作用域。
三、define与inline的区别
1.#define是关键字,inline是函数;
2.宏定义在预处理阶段进行文本替换,inline函数在编译阶段进行替换;
3.inline函数有类型检查,相比宏定义比较安全;

124、 printf实现原理?

1.在C/C++中,对函数参数的扫描是从后向前的。C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来,在计算机的内存中,数据有2块,一块是堆,一块是栈(函数参数及局部变量在这里),而栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在高地址,最后压入的参数在低地址,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。
2.printf的第一个被找到的参数就是那个字符指针,就是被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数来判断参数个数及数据类型,通过这些就可算出数据需要的堆栈指针的偏移量了,下面给出printf("%d,%d",a,b);(其中a、b都是int型的)的汇编代码.

125、 #include 的顺序以及尖括号和双引号的区别

1.<>尖括号表示编译器只在系统默认目录或尖括号内的工作目录下搜索头文件,并不去用户的工作目录下寻找,所以一般尖括号用于包含标准库文件;
2.""双引号表示编译器先在用户的工作目录下搜索头文件,如果搜索不到则到系统默认目录下去寻找,所以双引号一般用于包含用户自己编写的头文件。

126、 lambda函数

1.定义
利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象;每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符),我们称为闭包类型(closure type)。那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,其实一个右值。可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕捉模式以及变量,我们又将其称为lambda捕捉块。
2.用法
lambda表达式的语法定义如下:
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下:
capture list:捕获外部变量列表
params list:形参列表
mutable指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型
function body:函数体
3.lamda的几种捕获方式

捕获形式

说明

[]

不捕获任何外部变量

[变量名, …]

默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符)

[this]

以值的形式捕获this指针

[=]

以值的形式捕获所有外部变量

[&]

以引用形式捕获所有外部变量

[=, &x]

变量x以引用形式捕获,其余变量以传值形式捕获

[&, x]

变量x以值的形式捕获,其余变量以引用形式捕获


4.lambda必须使用尾置返回来指定返回类型,可以忽略参数列表和返回值,但必须永远包含捕获列表和函数体;

127、 模板类和模板函数的区别是什么?

函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。即函数模板允许隐式调用和显式调用而类模板只能显示调用。在使用时类模板必须加<T>,而函数模板不必。

128、 为什么模板类一般都是放在一个h文件中

1.模板定义很特殊。由template<…>处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。
2.在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来,所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码,于是连接器也黔驴技穷了。

129、 C++中类成员的访问权限和继承权限问题。

一、三种访问权限
○1public:用该关键字修饰的成员表示公有成员,该成员不仅可以在类内可以被 访问,在类外也是可以被访问的,是类对外提供的可访问接口;
○2private:用该关键字修饰的成员表示私有成员,该成员仅在类内可以被访问,在类体外是隐藏状态;
○3protected:用该关键字修饰的成员表示保护成员,保护成员在类体外同样是隐藏状态,但是对于该类的派生类来说,相当于公有成员,在派生类中可以被访问。
二、三种继承方式
○1若继承方式是public,基类成员在派生类中的访问权限保持不变,也就是说,基类中的成员访问权限,在派生类中仍然保持原来的访问权限;
○2若继承方式是private,基类所有成员在派生类中的访问权限都会变为私有(private)权限;
○3若继承方式是protected,基类的共有成员和保护成员在派生类中的访问权限都会变为保护(protected)权限,私有成员在派生类中的访问权限仍然是私有(private)权限。

130、 cout和printf有什么区别?

cout<<是一个函数,cout<<后可以跟不同的类型是因为cout<<已存在针对各种类型数据的重载,所以会自动识别数据的类型。输出过程会首先将输出字符放入缓冲区,然后输出到屏幕。
cout是有缓冲输出:cout << "abc " <<endl;或cout << "abc\n ";cout < <flush; 这两个才是一样的。endl相当于输出回车后,再强迫缓冲输出。flush立即强迫缓冲输出。printf是无缓冲输出。有输出时立即输出(lsy注:在嵌入式里尽量不用用printf打印信息,耗时!)

131、 重载运算符?

1、我们只能重载已有的运算符,而无权发明新的运算符;对于一个重载的运算符,其优先级和结合律与内置类型一致才可以;不能改变运算符操作数个数;
2、这几种 . :: ?: sizeof typeid ** 不能重载;
3、两种重载方式,成员运算符和非成员运算符,成员运算符比非成员运算符少一个参数;
4、下标运算符、箭头运算符必须是成员运算符;
5、引入运算符重载,是为了实现类的多态性;
6、当重载的运算符是成员函数时,this绑定到左侧运算符对象。成员运算符函数的参数数量比运算符对象的数量少一个;至少含有一个类类型的参数;
7、从参数的个数推断到底定义的是哪种运算符,当运算符既是一元运算符又是二元运算符(+,-,*,&);
8、下标运算符必须是成员函数,下标运算符通常以所访问元素的引用作为返回值,同时最好定义下标运算符的常量版本和非常量版本;
9、箭头运算符必须是类的成员,解引用通常也是类的成员;重载的箭头运算符必须返回类的指针;

132、 函数重载函数匹配原则

将函数匹配分为三个阶段,确定候选函数,确定可行函数,确定最佳匹配函数。
1.确定候选函数:需要和被调用的函数同名,并且其声明在调用点可见。
2.确定可行函数:本次调用传入的实参能够被候选函数使用。它要满足两个条件,一是形参数量和实参数量相同,二是每个实参的类型和对应形参类型相同或者能够转换成形参的类型。
3.寻找最佳匹配:最佳匹配最基本的思想是认为,实参类型越接近,它们就越匹配。

133、 定义和声明的区别

1.如果是指变量的声明和定义从编译原理上来说,声明是仅仅告诉编译器,有个某类型的变量会被使用,但是编译器并不会为它分配任何内存。而定义就是分配了内存。
2.如果是指函数的声明和定义声明:一般在头文件里,对编译器说:这里我有一个函数叫function()让编译器知道这个函数的存在。定义:一般在源文件里,具体就是函数的实现过程 写明函数体。

134、 全局变量和static变量的区别

1、全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。
2.这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个原文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。static全局变量与普通的全局变量的区别是static全局变量只初始化一次,防止在其他文件单元被引用。

135、 static函数与普通函数有什么区别?

在函数的返回类型前加上关键字static,函数就被定义成为静态函数。普通函数的定义和声明默认情况下是extern的,但静态函数被限定在源码文件中,只在声明他的文件当中可见,不能被其他文件所用。
普通函数:
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。
类成员函数:
区别1:静态成员函数实际上是一个全局函数,不依赖一个类的对象,属于类,不创建对象也可调用。
普通成员函数依赖一个类的对象,也就是它有一个隐藏的调用参数(this)指针,必须指向一个类的对象。
区别2:静态函数只能访问类中的静态成员变量;
区别3:如果类的成员函数想作为回调函数来使用,如创建线程等,一般只能将它定义为静态成员函数才行。
定义静态函数有以下好处:
<1> 其他文件中可以定义相同名字的函数,不会发生冲突。
<2> 静态函数不能被其他文件所用。

136、 静态成员与普通成员的区别

1.生命周期
静态成员变量从类被加载开始到类被卸载,一直存在;
普通成员变量只有在类创建对象后才开始存在,对象结束,它的生命期结束;
2.共享方式
静态成员变量是全类共享;普通成员变量是每个对象单独享用的;
3.定义位置
普通成员变量存储在栈或堆中,而静态成员变量存储在静态全局区;
4.初始化位置
普通成员变量在类中初始化;静态成员变量在类外初始化;
5.默认实参
可以使用静态成员变量作为默认实参,

137、 说一下理解 ifdef endif

1.一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
2.条件编译命令最常见的形式为:
#ifdef 标识符
程序段1
#else
程序段2
#endif
它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。其中#else部分也可以没有,即:#ifdef
程序段1
#denif
3.在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件上时,就会出现大量“重定义”错误。在头文件中使用#define、#ifndef、#ifdef、#endif能避免头文件重定义。

138、 隐式转换,如何消除隐式转换?

1.C++的基本类型中并非完全的对立,部分数据类型之间是可以进行隐式转换的。所谓隐式转换,是指不需要用户干预,编译器私下进行的类型转换行为。很多时候用户可能都不知道进行了哪些转换
2.C++面向对象的多态特性,就是通过父类的类型实现对子类的封装。通过隐式转换,你可以直接将一个子类的对象使用父类的类型进行返回。在比如,数值和布尔类型的转换,整数和浮点数的转换等。某些方面来说,隐式转换给C++程序开发者带来了不小的便捷。C++是一门强类型语言,类型的检查是非常严格的。
3.基本数据类型,基本数据类型的转换以取值范围的作为转换基础(保证精度不丢失)。隐式转换发生在从小->大的转换中。比如从char转换为int。从int->long。自定义对象 子类对象可以隐式的转换为父类对象。
4.C++中提供了explicit关键字,在构造函数声明的时候加上explicit关键字,能够禁止隐式转换。
5.如果构造函数只接受一个参数,则它实际上定义了转换为此类类型的隐式转换机制。可以通过将构造函数声明为explicit加以制止隐式类型转换,关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以无需将这些构造函数指定为explicit。

139、 虚函数的内存结构,那菱形继承的虚函数内存结构呢

菱形继承的定义是:两个子类继承同一父类,而又有子类同时继承这两个子类。例如a,b两个类同时继承c,但是又有一个d类同时继承a,b类。
lsy注:由于这部分内容太多,一定移步博客:(讲解深入浅出,推荐!最开始不知道怎么搞了个vip可看,又取消不了,可能后续会重写,如果你在看这篇文档的时候发现还不能看,就联系我)
https://blog.csdn.net/qq_41687938/article/details/120487045

140、 多继承的优缺点,作为一个开发者怎么看待多继承

1.C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承。
2.多重继承的优点很明显,就是对象可以调用多个基类中的接口;
3.如果派生类所继承的多个基类有相同的基类,而派生类对象需要调用这个祖先类的接口方法,就会容易出现二义性
4.加上全局符确定调用哪一份拷贝。比如pa.Author::eat()调用属于Author的拷贝。
5.使用虚拟继承,使得多重继承类Programmer_Author只拥有Person类的一份拷贝。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子木呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值