c++面试题汇总

时间:2024-05-23 09:13:15

1.new、delete、malloc、free关系.
2.delete与 delete []区别
3.对于子类来说,其父类、成员、自身的构造顺序和析构顺序?
4.介绍c++的多态
5.虚函数,纯虚函数
6.什么是“引用”?申明“引用”要注意哪些问题?
8.将“引用”作为函数参数有哪些特点?
9.在什么时候需要使用“常引用”? 
10.将“引用”作为函数返回值类型需要遵守的规则?
11.结构与联合有和区别?
12.有哪几种情况只能用intialization list ?
13.引用和指针有什么区别 ?
14.请说出const与#define 相比,有何优点?
15.内存的分配方式有几种?
16.基本类型数据范围(32位机)
17.类里面static和const可以同时修饰成员函数吗?
18.解释早绑定和晚绑定
20.main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?
21.如何判断一段程序是由c编译程序还是由c++编译程序编译的?
22.sizeof与strlen的区别?
23.野指针,空指针和悬垂指针的区别?
24.什么是智能指针?
25.如果虚函数是有效的,那为什么不把所有函数设为虚函数?
26.经常要操作的内存分哪几个类别?
27.举例说明static_cast如何使用?(会编译成cpu指令)
28.举例说明const_cast如何使用?(纯粹是编译器指令)
29.举例说明reinterpret_cast如何使用?(纯粹是编译器指令)
30.举例说明dynamic_cast如何使用?(唯一一个可能在允许时候转换失败)
31.typedef和#define联系和区别
32.内联函数和#define联系和区别
33. 链接指示:extern “C”的作用。
34.虚函数表放在什么地方?
35.模板特化的概念,为什么特化?
36.explicit的作用?
37.内存溢出有哪些因素?
38.举例加说明智能指针?
39.区别仿函数和函数指针
40.相关寄存器
41.相关函数调用约定
42.函数调用过程.(略)
43.静态成员函数为什么不能申明为虚函数
44.模板的实现和声明是否一定要在同一个头文件中,为什么?
45.构造函数和析构函数可以抛出异常吗?
46.STL是如何分配内存的?
47.STL是线程安全的吗?
48.数组和指针的区别?
49.Overload 重载,Override 重写 , Overwrite(Redefine)隐藏
50.请讲述堆和栈的区别
51.区别sprintf,strcpy,memcpy函数
52.面向对象设计SOLID五大原则

答案:

 

1.new、delete、malloc、free关系.

相同:

①都是在堆上申请内存

②申请的内存都是需要手动释放

区别:

①new/delete是运算符,malloc/free是库函数

②new要提供类型,返回的是指向该类型的指针,malloc只需要提供申请空间大小,返回的是

void*指针

③new/delete在分配空间之后会自动调用构造、析构函数。

 

2.delete与 delete []区别

①delete只会调用一次析构函数,delete[]会调用每个成员的析构函数。

②对于内置类型,没有析构函数,所以使用delete也不会造成内存泄漏。

③两者本质都是用过operator delete来释放内存。

 

3.对于子类来说,其父类、成员、自身的构造顺序和析构顺序?

①构造:先父类->再成员(按照申明顺序,如果有vptr,默认最先)->最后自身。

②析构:和上面相反。

 

4.介绍c++的多态

①多态是对于不同对象接收相同消息时产生不同的动作。

②C++的多态性具体体现在运行和编译两个方面。

③运行时的多态性:通过继承和虚函数来体现。

④编译时的多态性:通过函数和运算符的重载,模板上。

 

5.虚函数,纯虚函数

虚函数:

①在基类中冠以关键字 virtual 的成员函数。

②允许子类进行重写,是C++运行时多态的方法。

②通过多态调用(基类指针或者基类引用)虚函数,会根据实际类型来调用对应版本。

纯虚函数:

①纯虚函数是在基类通过virtual xxx() = 0保留一个函数名字。

②含有纯虚函数的类是抽象类,不能实例化,只能够被继承。

③继承抽象类的派生类必须实现抽象类的纯虚函数。

④在c++中纯虚函数可以用来实现Java,C#中的接口interface概念。

 

6.什么是“引用”?申明“引用”要注意哪些问题?

概念:

①引用就是某个目标变量的"别名",对应用的操作与对变量直接操作效果完全相同。

②引用的底层实现是指针。所占内存大小也就是一个指针的大小。

申明:

③引用在申明的时候必须进行初始化,且一旦初始化后就不能"绑定"到其他变量上。

④在类中成员存在引用,必须要在类的构造列表中进行初始化。

⑤不能申明、定义引用数组。//引用是别名,没有自己的内存,定义数组需要申请内存

 

8.将“引用”作为函数参数有哪些特点?

①传递引用跟传递指针效果一样,在函数中对引用做的改变会影响引用实体变量。

②当传递的变量是所占内存空间较大时候,实参到形参的拷贝是很费时间。如果传递对象还要

调用拷贝构造函数。因此,传递引用时间效率要高!(不适用于内置类型、指针、STL迭代器)

 

9.在什么时候需要使用“常引用”? 

①如果传递引用的数据不希望在函数中改变,就应该使用常引用。

②如果传递实参可能是右值的时候,就必须要使用常引用。(否则会编译错误)

 

10.将“引用”作为函数返回值类型需要遵守的规则?

①不能返回函数局部非静态变量的引用:局部变量会在函数调用返回后被销毁,因此返回的引

用就是"无所指"的应用,这时未定义的行为。

②尽量不要返回函数内部new出来的空间引用:因为很容易造成内存泄漏(假如函数返回值被

当作临时对象,这个引用所指的内存就无法释放。

③可以返回类成员函数的引用,不过如果外界不改变其属性和状态的话尽量申明为const。

④对于某些无法拷贝的对象要将返回值申明为引用(重载流操作符的时候)。

 

11.结构与联合有和区别?

①结构和联合都是由不同的数据类型成员组成。

②联合在任何同一时刻都只有一个成员有效(所有成员共用一块内存地址)。结构所有成员都有

自己独有的存放地址。

③对于联合不同成员的赋值会对其他成员造成重写,而结构不同成员赋值是互不影响。

 

12.有哪几种情况只能用intialization list ?

①当成员含有const、reference时候。

②需要调用基类非默认构造函数时候。

 

13.引用和指针有什么区别 ?

①引用必须初始化,指针不必。

②引用初始化后不能够重新绑定其他变量,而指针可以重新指向其他变量。

③不存在指向空值的引用,但是存在NULL空指针。

④sizeof(引用)得到的是目标变量大小,sizeof(指针)得到的是指针大小。

⑤有的运算符作用的意义不一样(++,--)

⑥指针有多重指针的概念,而引用没有。

 

14.请说出const与#define 相比,有何优点?

①const 常量有数据类型,而宏常量是没有数据类型。编译器可以对前者进行类型安全检查,

而后者仅仅进行字符替换,没有类型安全检查,并且在字符替换过程中会产生一些意料不到的

错误。(边际效应)

②有些集成化调试工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++程序

中一般只使用const常量而不是用#define。

 

15.内存的分配方式有几种?

①从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期

间都存在。例如全局变量。

②在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束

时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配

的内存容量有限。

③从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,

程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵

活,但问题也最多。

 

16.基本类型数据范围(32位机)

①void : 0byte 无值域

②bool: 1byte true,false

③short: 2byte -2^15~2^15-1

④unsigned short: 2byte 0~2^16-1

⑤int 4byte -2^31~2^31-1

⑥unsigned int: 4byte 0~2^32-1

⑦long long 8byte -2^63~2^63-1

⑧float 4byte -3.4E-38~3.4E+38

⑨double 8byte -1.7E-308~1.7E308

 

17.类里面static和const可以同时修饰成员函数吗?

C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员函数为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的。

 

18.解释早绑定和晚绑定

多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。

 

19.STL容器比较

c++面试题汇总

 

20.main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

atexit函数  

注册终止函数(即main执行结束后调用的函数) 

void atexit(void (*func)(void));  参数为函数指针  当做参数的函数返回值和参数都为空

exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。

 

21.如何判断一段程序是由c编译程序还是由c++编译程序编译的?

#ifdef __cplusplus

  cout<<"c++";

#else

  cout<<"c";

#endif

 

22.sizeof与strlen的区别?

①sizeof是运算符,而strlen是函数;

②sizeof可以用类型做参数,其参数可以是任意类型的或者是变量,而strlen只能用

char*做参数,且必须是以’\0’结尾;

③sizeof的结果是编译时的常量,而strlen要到运行时候才能计算出来。

④数组作为sizeof的参数不会被退化为指针,而传递给strlen会退化。

 

23.野指针,空指针和悬垂指针的区别?

①野指针是指未初始化的指针。

②空指针是指被赋值为NULL/nullptr的指针。

③悬垂指针是指指向了已经被删除的对象的指针。

④空指针可以反复delete,但是对野指针和悬垂指针进行delete会产生未定义行为。

⑤使用野指针、空指针和悬垂指针都会产生未定义行为。

 

24.什么是智能指针?

①智能指针是C++管理资源的(RAII,资源获取就是初始化)方法的一个体现。

②智能指针是对普通指针的封装;在构造时候获取指针地址,在析构时候释放指针所指资源。

③c++98提供了auto_ptr,c++11提供了shared_ptr,unique_ptr,weak_ptr,不推荐auto_ptr。

④总的说来,智能指针就是为了方便管理动态内存,防止内存泄漏的一个手段。

 

25.如果虚函数是有效的,那为什么不把所有函数设为虚函数?

虚函数是有代价的,由于每个虚函数的对象都要维护一个虚函数表。

②虚函数在使用动态调用的时候会带来运行时性能负担(多一次寻址)。所以对于不需要多态的

函数来说这些负担是不必要的。

 

26.经常要操作的内存分哪几个类别?

①栈区:由编译器自动分配和释放,存放函数的参数值、局部变量的值等;

②堆:一般由程序员分配和释放,存放动态分配的变量;

③全局区(静态区):全局变量和静态变量存放在这一块,初始化的和未初始化的分开放;

④文字常量区:常量字符串就放在这里,程序结束自动释放;

⑤程序代码区:参访函数体的二进制代码。

 

27.举例说明static_cast如何使用?(会编译成cpu指令)

①static_cast大部分情况下用于对隐式转换的显示化,同时去掉相关warning警报。

②static_cast可以将左值转化为左值引用或者右值引用,将右值转化为有值引用,实现移动语

③static_cast可以用于子类与父类(非虚拟)指针、引用之间的转换,不过下行转换可能是不安

全的。

④static_cast可以用于内置类型的转换(非指针,引用),任何类型的point和void*互相转换。

⑤static_cast可以用于将nullptr或者std::nullptr_t类型的值转化为任意指针类型的空指针。

⑥static_cast不能转换掉expression_r_r的const、volitale、或者__unaligned属性

⑦区别:c型强制转换可以作用于没联系的指针之间的转换,而static_cast会有类型检查。

 

28.举例说明const_cast如何使用?(纯粹是编译器指令)

①"去掉"类型的const或者volatile属性。

②指向同一类型的二级或者多级指针相互转换,无论const volatile属性在哪一级。

③任意类型T的左值可以转化为一个同样类型的左值或者右值,无论const volatile属性有无。

④任意类型T的右值可以转化为一个同样类型的右值,无论const volatile属性有无。

⑤函数指针或者成员函数指针不能使用const_cast转换。

⑥如果通过const_cast来获取一个const obj的no-const权限或者volatile obj的no-volatile权

限来修改、影响源obj都是未定义的行为。

⑦一般推荐是no-const,no-volatile的对象但是通过某个接口得到的是其const引用,可以使

用const_cast来去掉const volatile后再来使用。

 

29.举例说明reinterpret_cast如何使用?(纯粹是编译器指令)

①将任意类型指针转化为整形(要求住足够大),可以把整形转化为任意类型指针。

②不同类型指针之间相互转换(包括函数指针)。

③类型为T1的表达式可以转化为类型T2的引用,但是只有在类型别名规则允许下安全访问。

④如果不同函数类型的指针相互转换,调用转换后的指针是未定义行为。

⑤在VC中,reinterpret_cast被用来辅助哈希函数。

⑥有的时候提供的数据类型和接口的参数类型不匹配(比如int to uint\double)等,如果使用

static_cast会有精度损失,这个时候就可以使用reinterpret_cast

⑦reinterpret_cast如果不正确的使用很容易导致程序的不安全。

 

30.举例说明dynamic_cast如何使用?(唯一一个可能在允许时候转换失败)

①下行转化的时候,源对象必须是多态类类型。

②如果目标类型为指针,转换失败会返回空指针。如果目标类型为引用,转换失败会抛出与

std::bad_cast类型得处理程序相匹配的异常。

③一般用于在多态的继承体系中,父类指针\引用安全的转换到子类的指针\引用。

④如果在构造\析构函数中使用,而表达式指向或者引用当前正在构造\析构的对象,那么目标

类型只能是当前自身类或者其基类。

⑤dynamic_cast会带来一定的运行成本(RTTI),所以在能够使用static_cast的时候尽量使用

static_cast。

⑥可以通过dynamic_cast的结果是否为空来确定父类指针指向的是继承体系中哪个子类。

 

31.typedef和#define联系和区别

联系:

①都可以实现用一个新标识符代替一个已知类型的名字。

区别:

①标识:#define是预编译指令,只在预编译阶段做文本替换;typedef是一个存储类的关键

字(auto,extern,mutable,static,register)一样,所以没法typedef static int sint;编译器会提

示指定了多个存储类。

②执行时间:#define是在预编译阶段通过文本替换完成,在编译期间新标识符完全不可见,

不做类型安全检查;typedef是在编译阶段给一个已知类型取一个别名。

③本质:#define只是文本替换,不是别名,typedef是以前类型的别名是完整的。所以,

typedef int* pint; 对于const pint实质是int*const而不是const int*。

④作用域:#define从定义到文件末尾(undef)有效,typedef有自己的作用域(类似于变量)。

⑤作用:#define可以做很多事情:别名,定义常量,编译开关。typedef只是用来定义别名。

 

32.内联函数和#define联系和区别

联系:

①都可以节省在函数调用方面所带来的时间和空间开销。二者都采用了空间换时间的方式,在

其调用处进行展开。

区别:

①本质:#define是预编译指令,只在预编译阶段做文本替换;内联函数是在编译阶段在调用

出展开。后者会进行参数类型安全检查,前者不会。

②重载:#define不能进行函数重载,内联函数可以进行函数重载。

③作用域:#define从定义到文件末尾(undef)有效,内联函数有自己的作用域(类,命名空

间,全局)。

④参数:#define的函数参数只是进行替换,不会占用内存,有陷阱(++,--),而内联会先对参

数求值,会占用内存。

 

33. 链接指示:extern “C”的作用。

①起因:因为C语言是不支持函数重载,而c++语言是支持函数重载,主要的一个原因是因为

C++语言对函数进行编译过程中会把函数名字加上其参数列表等标识,使得不同参数列表的

同名函数进行重载。所以说就究其本质是C和C++对函数的编译和链接方式不一样。

②作用:所以如果C++中如果想使用C的函数,就必须使用extern"C"来使得引入的(头文

件,DLL)按照C的编译、链接方式进行,否则在链接过程中会提示找不到目标函数。

 

34.虚函数表放在什么地方?

①gcc编译器将虚函数表放在了只读数据段(.rodata)

②msvc编译器将虚函数表放在了常量段。

 

35.模板特化的概念,为什么特化?

①C++中的模板特化不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板

的特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。

②有时候通用的base template并不能适用于所有type,对于有的type,我们希望能够"多态"的

执行不一样的操作,这个时候就应该使用特化,这样编译器就会在使用模板的时候匹配最"具

体"的版本。

 

36.explicit的作用?

①类中如果有只有一个参数的构造函数,那么表示这个参数类型可以隐式转化为该类型。

②有的时候为了避免这种隐式转换带来的不容易察觉的错误,就可以在构造函数前用explicit

修饰,这样编译器就会阻止隐式转换。

 

37.内存溢出有哪些因素? 内存泄漏

①表达式结果超过存储类型的范围。

②数组访问越界,字符串拷贝容量不够等等。

③栈空间申请内存过大,递归层数过多,线程过多。

④内存泄漏,无法释放申请的内存。

⑤死循环

 

38.举例加说明智能指针?

①auto_ptr: 不能复制,复制之后原智能指针资源所有权会被剥夺,不能管理动态数组

②shared_ptr: 复制使用引用技术手段,当资源没有被引用时候自动删除,缺点:循环引用。

③intrusive_ptr : 比shared_ptr效率高,但是需要自己维护引用计数器。

④unique_ptr: 和auto_ptr,不过可以管理动态数组,c++11中明确取代了auto_ptr

⑤weak_ptr: 弱引用智能指针,不会增加计数,配合shared_ptr的工具。避免循环引用。

 

39.区别仿函数和函数指针

①函数指针不用解释了,仿函数就是重载了()的类,使得其行为可以跟函数调用一样。

②函数指针比较灵活可以随时指向不同的函数。

③仿函数可以保存状态,使得仿函数可能在不同的情况下表现不同的行为。

④仿函数可以使用内联,所以有的时候使用仿函数要比函数指针要快。

⑤仿函数一般是跟模板泛型编程搭配使用,再配合适配器(bind等)非常灵活。

 

40.相关寄存器

①eax : 累加及寄存器,常常用作函数返回值

②ebp : 基址指针寄存器,常常指向当前栈底部。 栈底指针

③esp : 堆栈(Stack)指针寄存器,指向堆栈顶部 栈顶指针

④ecx : 计数器(counter),常常用做字符串,循环操作的计数器,this指针。

⑤eip : 指令寄存器,指向下一条指令的地址 指令指针

⑥ebx: 基址寄存器

⑦esi: 源变址寄存器

⑧edi: 目的变址寄存器

 

41.相关函数调用约定

①_cdecl:按照从右到左压参数入栈,由调用者把参数弹出栈。是c/c++缺省调用方式。

②_fastcall: 通过寄存器ecx,edx传送前双个字或更小参数,剩下仍旧从右到左压栈,被调用的函数在返回前清理内存栈。

③__stdcall:按照从右到左压参数入栈,由函数自身把参数弹出栈。是win api的方式

④_thiscall:类成员函数调用方式,vc是将this指针放入ecx寄存器,gnu gcc把this当作第一个

参数。如果参数个数确定由函数返回前清理内存栈,否则由调用端清理。

 

42.函数调用过程.(略)

c++面试题汇总

_cdecl:

1.从右向左压入参数

2.压入调用函数的下一条指令(为了返回)

3.压入被调用函数地址,跳转(EBP,ESP,EIP都会切换到子函数的栈区)

 

 

 

43.静态成员函数为什么不能申明为虚函数

①因为静态成员函数实质就是添加了一层命名空间的普通函数,调用不需要传入this指针。

②虚函数表现的多态主要是根据this指针的真实类型,而静态成员函数根本没有this指针。

 

44.模板的实现和声明是否一定要在同一个头文件中,为什么?

①是的,因为在模板实例化的时候,编译器会在模板头文件里去找所有函数的定义并且把模板

类型用真实类型替换,如果实现没有放在头文件,那么编译器就无法正确访问到。就会实例化

失败。

②可以提供分离申明的方式,就是将模板声明写在一个头文件里exm: tem.h,然后把所有实现

写在另一个文件exm:tem.tpp。然后在tem.h声明之后#include<tem.tpp>

③还有一种方法就是按照常规的将声明写在.h头文件,实现写在.cpp源文件,只不过值得注意

的是你必须在cpp源文件中将你需要的模板实例显示实例化。

 

45.构造函数和析构函数可以抛出异常吗?

①可以通过在构造函数里抛出异常来监测对象是否创建成功,但是需要注意的是在抛出异常之

前释放申请的资源。构造函数抛出异常是不会调用析构函数的。

②析构函数不可以抛出异常,通常异常发生时,c++的机制会调用已经构造对象的析构函数来

释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成

程序崩溃的问题。如果无法保证不抛出异常,可以使用catch语句在析构函数内将异常捕获。

 

46.STL是如何分配内存的?

在SGI STL中才用了双层级配置器,第一级配置器直接使用malloc()和free()向系统申请堆内

存。第二级配置器则采用不同的策略。

如果申请的内存大于128byte的时候就直接启动第一级分配器。如小于128byte就启动第二级

分配器。第二级别分配器是维护了一个长度为16的节点,每个节点表示大小为8,16,

24....128byte内存池。每次申请内存都是向上转为8的整数倍,然后到对应池子取相应大的内

存。每个池子也是一个链表,通过二级分配器分配的内存,在回收的时候并不直接交还给系统

而是放回池子,由池子最后自己释放。

 

47.STL是线程安全的吗?

STL不是线程安全的

线程安全的情况

  • 多个读取者是安全的。多线程可能同时读取一个容器的内容,这将正确地执行。当然,在读取时不能 有任何写入者操作这个容器。
  • 对不同容器的多个写入者是安全的。多线程可以同时写不同的容器。

线程不安全的情况

  • 在对同一个容器进行多线程的读写、写操作时。

 

安全措施:

  • 在每次调用容器的成员函数期间都要锁定该容器。
  • 在每个容器返回的迭代器(例如通过调用begin或end)的生存期之内都要锁定该容器。
  • 在每个在容器上调用的算法执行期间锁定该容器。(这事实上没有意义,因为,正如条款32所解释的,算法没有办法识别出它们正在操作的容器。不过,我们将在这里检验这个选项,因为它的教育意义在于看看为什么即使是可能的它也不能工作。)
  •  

48.数组和指针的区别?

①位置:数组要么在静态区被创建(全局或者static),要么在栈上面创建(局部)。而指针可以指

向任意类型内存块。

②sizeof():数组得到的是容量(字节数),而指针只能得到指针变量的字节数。

③数组在参数传递过程中会退化为指针。

 

49.Overload 重载,Override 重写 , Overwrite(Redefine)隐藏

①overload:

同一个作用域(同一个类中),函数名相同,不同参数列表,返回值可以不同。

编译时多态的体现,调用时根据实参类型匹配相应函数实体。

②override:

不同作用域(父类,子类),函数名相同,参数相同,必须是虚函数。返回值相同(协变)。

运行时多态体现,调用时根据实际类型选择对应的虚函数实体。

③overwrite(redefine):

不同作用域(父类,子类),函数名相同,参数不同(无论是否虚函数)或者参数相同(基类没

有virtual)。

父类同名函数被隐藏,通过子类无法调用基类的同名函数。

 

50.请讲述堆和栈的区别

①申请方式不同。栈上有系统自动分配和释放;堆上有程序员自己申请并指明大小。

②栈是向低地址扩展的数据结构,大小很有限;堆是向高地址扩展,是不连续的内存区域,空间相对大且灵活;

③栈由系统分配和释放速度快;堆由程序员控制,一般较慢,且容易产生碎片;

④栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示溢出;

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

51.区别sprintf,strcpy,memcpy函数

①strcpy 函数操作的对象是字符串,完成从源字符串到目的字符串的拷贝功能。

②sprintf 函数操作的对象不限于字符串,虽然目的对象是字符串,但是源对象可以是字符

串、也可以是任意基本类型的数据。这个函数主要用来实现(字符串或基本数据类型)向字符串的转换功能。如果源对象是字符串,并且指定 %s 格式符,也可实现字符串拷贝功能。

③memcpy 函数顾名思义就是内存拷贝,实现将一个内存块的内容复制到另一个内存块这一

功能。内存块由其首地址以及长度确定。

 

52.面向对象设计SOLID五大原则

S = 单一职责原则 Single Responsibility Principle

O = 开放闭合原则 Opened Closed Principle

L = Liscov替换原则 Liscov Substitution Principle

I = 接口隔离原则 Interface Segregation Principle

D = 依赖倒置原则 Dependency Inversion Principle