c/c++面试概念性问题总结

来源:互联网 发布:淘宝店宝宝靠谱吗 编辑:程序博客网 时间:2024/05/16 15:00
                             面试中概念性问题总结
1、new  delete和malloc free的区别与联系?

面试中可以这样回答:

都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的析构函数,而free 不会调用对象的析构函数。  

 

详细讲解:

①malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都用于在堆(heap)上进行动态的内存操作(申请动态内存和释放内存)。

②delete不仅会释放空间,在释放前会调用析构函数,和new对应,new调用构造函数,free只会释放内存。

③ 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象      在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

④ C++程序经常要调用C函数,其实new/delete内部实现也调用了malloc/free。
而C程序只能用malloc/free管理动态内存

⑤ new是强制类型的,不需要考虑类型,而malloc不是,它返回的指针是void*型,必须要强转成需要的类型。(注意)

⑥ new 可以调用构造函数在声明的时候初始化,malloc只是分配空间,需要在其他地方初始化。而且malloc需要指定分配空间大小, 而new是自动计算的。

⑦new/delete是可以重载的,而重载之后,就成为了函数。当new/delete在类中被重载的时候,可以自定义申请过程,比如记录所申请内存的总长度,以及跟踪每个对象的指针。

⑧free和delete可以释放NULL指针。

 

举例说明:

Stu* stu = malloc(sizeof(Stu));   // Stu是一个类

采用malloc初始化,你将不会调用Stu的构造方法,而只是单纯的分配空间。而且我们只认为你是分配一个空间,而不是想创建一个对象。(用于struct)

Stu * stu = new Stu ();则会调用Stu的构造方法来初始化对象,也就是说你既要分配空间又要初始化这段空间,让它变成一个对象。(用于class)

 

delete与delete[]的区别

delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。当delete操作符用于数组时,它为每个数组元素调用析构函数, delete与new配套,delete []与new []配套。

 提醒:关于new 、delete、malloc、free的区别,大家尽量多的记忆几点,面试的时侯可以多回答几点。

2、  C++中的class和struct的区别:
   从语法上,在C++中(只讨论C++中)。class和struct做类型定义时只有两点区别:
   ① 默认继承权限:class的继承按照private继承处理,struct的继承按照public继承处理。
   ② 成员的默认访问权限:class的成员默认是private权限,struct默认是public权限。
 
   C++和C中struct的区别:
   在C++中struct可以定义方法,C中不可以。

 

3、 static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

原理分析:

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

② 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。

③static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

另外static函数与普通函数的一个区别是:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

 

简要回答:

① 限制变量的作用域(文件级的)。

② 设置变量的存储域(全局数据区)。

 

比对回答:

把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。static局部变量只被初始化一次,下一次依据上一次的结果值。

把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。static全局变量只初始化一次,防止在其他文件单元中被引用。普通全局变量在其他文件中也可用,需要用extern声明。

   (根据问题倾向作答)

 

4、局部变量能否和全局变量重名?

能,局部会屏蔽全局。要用全局变量,需要使用"::"。

 

 

 

 

 

5、如何引用一个已经定义过的全局变量?

   可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错;如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

 

6、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

   可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

7、关于const的考题

const int a;

int const a;

const int *a;

int * const a;

int const * aconst;

前两个的作用是一样,a是一个常整型数。

第三个a是一个指向常整型数的指针(即,整型数是不可修改的,但指针可以)。

第四个a是一个指向整型数的常指针(即,指针指向的整型数是可以修改的,但指针是不可修改的)。

最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的,但是基本上不会应用这种形式)。

 

8、const#define相比有何优点

① const 常量有数据类型,而宏常量没有数据类型。编译器可以对const修饰的数据进行类型安全检查。而对宏常量只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。

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

 

9、几种内存分配方式以及它们的区别

① 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

 ② 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令。

③ 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

 

10、main函数执行以前还会执行什么代码?

    全局对象的构造函数会在main函数之前执行。

 

11、C++是不是类型安全的语言?

不是,两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

 

12、结构体(struct)与联合(union)的区别:

       ① 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在,而且不同成员的存放地址不同。 

② 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

     (掌握结构中数据成员的对齐、补齐问题,及sizeof结构和联合的区别)

 

13、引用与指针的区别是什么?

简单回答:

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

② 引用初始化以后不能被改变,指针可以改变所指的对象。

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

    作为函数参数时的区别:

① 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。

② 传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

③ 使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

④ 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

(尽量整理成自己的语言,面试的时候可以做到有条理的回答)

 

14、什么是引用,声明和使用引用时需要注意哪些事项?

        引用就是一个变量的别名,对引用的操作等同于对变量本身的操作。它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。

      ① 声明一个引用时一定要对它初始化。

      ② 一个引用不能声明为两个变量的别名。

③ 不能建立引用数组,可以建立数组的引用。

④ 在函数中不能返回局部变量的引用。(当一个局部变量为静态时,可以返回该局部变量的指针,但是引用同样不可以)

⑤ 不能返回函数内部new分配的内存的引用。

 

 

 

 

 

15实现多态的方法?

      ① 一个基类的引用可以指向它的派生类实例

      ② 一个基类的指针可以指向它的派生类实例

 

16、面向对象的三个基本特征,并简单叙述之?

① 封装:将客观事物抽象成类,每个类对自身的数据和方法实行访问控制(private,protected,public)

② 继承:广义的继承有三种实现形式:

实现继承(指使用基类的属性和方法而无需额外编码的能力)

可视继承(子窗体使用父窗体的外观和实现代码)

接口继承(仅使用属性和方法,实现滞后到子类实现)。

前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。

③ 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

     补充问题:

     多态的作用?

主要是两个:

1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;

2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

 

 

17、重载(overload)和重写(overried,有的书也叫做覆盖)的区别?

① 重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

② 重写:是指子类重新定义父类虚函数的方法。

 

从实现原理上来说:

① 重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。对于这类函数的调用,在编译期间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!

② 重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)

18、多态的作用?

主要是两个:

① 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;

② 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

 

19、有哪几种情况只能用初始化列表而不能使用赋值。

 当类中含有const成员变量和引用成员变量时,基类的构造函数都需要初始化表。

 

20、类成员函数的重载、覆盖和隐藏的区别?

① 成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

② 覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual关键字。

③ “隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

 

21、 堆栈溢出一般是由什么原因导致的?

① 没有回收垃圾资源

② 层次太深的递归调用

 

22、h头文件中的ifndef/define/endif 的作用?

防止该头文件被重复引用。

 

23C++中的空类,默认产生的类成员函数:

class

{

  public:

    Empty();                         // 缺省构造函数

    Empty(constEmpty&);             // 拷贝构造函数

    ~Empty();                        // 析构函数

    Empty&operator=(const Empty&);  // 赋值运算符

    Empty*operator&();              // 取值运算符

    const Empty*operator&() const;  // 取值运算符const

};

 

24、.进程间通信的方式有?

进程间通信的方式有 :共享内存,管道(有名管道/无名管道),Socket ,消息队列,信号,信号量,内存映射等。

 

 

25、进程死锁的原因?

    资源竞争及进程推进顺序非法。

 

26、死锁的四个必要条件?

     互斥,请求保持,不可剥夺,环路。

 

27、类的静态成员和非静态成员有什么区别?

类的静态成员每个类只有一个,即是属于本类的;类的非静态成员每个对象都有一份。

 

28、什么是浅拷贝?什么是深拷贝?

   浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。

一般来说,浅拷贝就是复制那个对象的指针。深拷贝就是复制了那个对象。

 

29、那些情况需要调用拷贝构造函数,请举例?

      a.一个对象以值传递的方式传入函数体

      b.一个对象以值传递的方式从函数返回

      c.一个对象需要通过另一个对象进行初始化

 

 

30、CC++有什么不同?

① 从机制上:c是面向过程的(但c也可以编写面向对象的程序);c++是面向对象的,提供了类。但是,c++编写面向对象的程序比c容易

② 从适用的方向:c适合要求代码体积小的,效率高的场合,如嵌入式;c++适合更上层的,复杂的;linux核心大部分是c写的,因为它是系统软件,效率要求极高。

③ C语言是结构化编程语言,C++是面向对象编程语言。C++侧重于对象而不是过程,侧重于类的设计而不是逻辑的设计。

 

31、C++virtualinline的含义分别是什么?
  在基类成员函数的声明前加上virtual关键字,意味着将该成员函数声明为虚函数。

 inline与函数的定义体放在一起,使该函数称为内联。inline是一种用于实现的关键字,而不是用于声明的关键字。

虚函数的特点;如果希望派生类能够重新定义基类的方法,则在基类中将该方法定义为虚方法,这样可以启用动态联编。

内联函数的特点;使用内联函数的目的是为了提高函数的运行效率。内联函数体的代码不能过长,因为内联函数省去调用函数的时间是以代码膨胀为代价的。内联函数不能包含循环语句,因为执行循环语句要比调用函数的开销大。

一个函数能否即是虚函数又是内联函数?

可以,建议不使用?

 

 

 

32、数组和链表的区别?

数组的数据是顺序存储的,存储大小固定。

链表的数据可以随机存储,存储大小可以动态的改变。

33、C++容器的分类及各自的特性?

C++容器分为顺序容器和关联容器。二者的本质区别在于:关联容器通过键(key) 存储和读取元素,而顺序容器的元素排列次序与元素值无关,是由元素添加到容器的顺序决定的,通过元素在容器中的位置顺序存储和读取元素。

标准库定义了三种顺序容器类型,包括:vector、list、deque。

容器内的元素类型必须至少满足2个条件:可复制和可赋值。

vector是连续存储的,能支持快速随机访问,list不需要连续存储,能在中间快速插入和删除,deque是双端队列,具有后进先出的特性。

所有的迭代器范围都是左闭合区间,  [begin,end)  包括begin,但不包括end

     list迭代器不支持算术运算,不支持关系运算,也没有下标操作,只有最简单的自增 自减相等 不等 运算。

标准库还提供了三种容器适配器:后进先出的stack、先进先出的queue和有优先级管理的priority_queue。stack可以建立在vector、list、deque上,queue要能提供push_front操作,不能建立在vector上,priority_queue要求提供随机访问能力,只能建立在vector或deque上。

 

标准库提供的关联容器有map、set、multimap和multiset。

map存储键值对,set存储单个键,multimap和multiset支持同一个键多次出现。

set与map不同的地方在于:set仅有key_type类型,它的value_type 也就是key_type;而且set不提供下标操作。map支持下标操作,而且与下标访问数组或者vector的行为截然不同:用下标访问不存在的元素将导致在map容器中添加一个新元素。

 

34、windows应用程序的类型?

控制台程序、窗口程序、库程序(静态/动态)

 

35、窗口程序的创建过程?

a.定义WinMain入口函数  b. 定义窗口处理函数 WindowProc 

c. 注册窗口类RegisterClass  d. 创建窗口CreateWindow

e. 显示窗口ShowWindow/UpdateWindow

 f. 消息循环GetMessage

                TranslateMessage

                DisptachMessage

g. 消息处理

 

36、win32程序的执行机制?

Win32窗口程序是采用事件驱动 方式执行,也就是 消息机制。

(程序的执行机制分为过程驱动和事件驱动)

 

 

 

 

37、列举win32中与消息相关的几个函数?

GetMessage - 获取消息

TranslateMessage- 翻译消息。将按键消息,翻译成字符消息

DispatchMessage- 派发消息。将消息派发到该消息所属窗口的窗口处理函数上。

 

38、列举几个常用的窗口消息?

    WM_DESTROY、WM_SYSCOMMAND、WM_CREATE、WM_SIZE、WM_QUIT、WM_PAINT等。

 

39、Windows中消息可以分为几类?

    系统消息——由系统定义好的消息,可以在程序中直接使用。

    用户自定义消息——由用户自己定义,满足用户自己的需求。由用户自己发出消息,并响应处理

    应用程序消息——程序之间通讯时使用的消息。

    系统注册消息——在系统注册并生成相应消息,然后可以在各个程序中使用这个消息。

 

40、消息队列的类型?

    系统消息队列——由系统维护的消息队列。存放系统产生的消息,例如鼠标、键盘等。

程序消息队列——属于每一个应用程序(线程)的消息队列。由应用程序(线程)维护。

 

41、Windows程序的入口是哪里?写出Windows消息机制的流程。

入口在WinMain()函数。

Windows应用程序消息处理机制:

A. 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中

B. 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。

C. 应用程序调用DispatchMessage,将消息回传给操作系统。

D. 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。

 

42、SendMessage()与PostMessage()之间的区别是什么?

它们两者是用于向应用程序发送消息的。PostMessagex()将消息直接加入到应用程序的消息队列中,不等程序返回就退出;而SendMessage()则刚好相反,应用程序处理完此消息后,它才返回。

 

43、函数assert的用法?
断言assert是仅在debug版本起作用的宏,用于检查“不应该“发生的情况。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。

 

44、Windows编程线程同步的几种方式?(重要)

    原子锁、临界区(段)、事件、互斥(体)、信号量、可等候定时器

 

 

 

 

45、关键字volatile有什么含意?并给出三个不同的例子。(嵌入式常考,了解)

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

      1) 并行设备的硬件寄存器(如:状态寄存器)

      2) 一个中断服务子程序中会访问到的非自动变量

      3) 多线程应用中被几个任务共享的变量

 补充问题:

1) 一个参数既可以是const还可以是volatile吗?解释为什么。

可以。

一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2) 一个指针可以是volatile 吗?解释为什么。

可以。

尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

 

 

0 0
原创粉丝点击