C/C++ 面试

C/C++ 面试

在一步一步走向梦想的程途里,袭来的是孤独是无助是无奈是伤感是犹豫,是彷徨。于是我们开始学会忍耐坚持,开始学会坚强勇敢。开始去明白梦想是一段孤独的旅程,是独自面对的未来。

一、C和C++的区别?

C++ 在 C 的基础上增添类,C 是一个结构化语言,它的重点在于算法和数据结构。C 程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于 C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

二、面向过程、面向对象、面向接口、面向切面的区别

面向过程:典型的是 C/C++ 的结构体,结构体里只有变量,没有处理变量的方法,需要专门编写处理变量的方法。
面向对象:ArrayList list=new ArrayList(); 坏处是如果改为LinkedList,所有代码可能需要重写,同时扩展一个新的List的话,需要重新调用。
面向接口:List=new Arraylist(); 是面向对象的一种形式,广义上讲,抽象类也是接口的一种形式,使用该类的某种属性(接口)来表示,降低耦合性,增加代码复用性。
面向切面:这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

三、什么是多态?

多态是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态。

四、设计模式懂嘛,简单举个例子?

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用于:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
工厂模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
适用于:当一个类不知道它所必须创建的对象的类的时候;当一个类希望由它的子类来指定它所创建的对象的时候;当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

五、STL库用过吗?常见的STL容器有哪些?算法用过哪几个?

STL包括两部分内容:容器和算法。(还有融合这二者的迭代器)
容器,即存放数据的地方。容器分为两类:序列式容器和关联式容器。
序列式容器,其中的元素不一定有序,但都可以被排序。如:vector、list、deque、stack、queue、heap、priority_queue、slist。
关联式容器,内部结构基本上是一颗平衡二叉树。所谓关联,指每个元素都有一个键值和一个实值,元素按照一定的规则存放。如:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap。
vector:它是一个动态分配存储空间的容器。区别于c++中的array,array分配的空间是静态的,分配之后不能被改变,而vector会自动重分配(扩展)空间。
set:其内部元素会根据元素的键值自动被排序。区别于map,它的键值就是实值,而map可以同时拥有不同的键值和实值。
算法,如排序,复制,以及个容器特定的算法。
迭代器是STL的精髓,迭代器提供了一种方法,使它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构。它将容器和算法分开,好让这二者独立设计。

六、数据结构会吗?项目开发过程中主要用到那些?

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
数据结构可以从两个方面分析:逻辑结构与物理结构(存储结构)。
其中逻辑结构指的是数据的组织方式,物理结构指的是数据在内存上的存储方式。
逻辑结构分为四种:集合结构,线性结构,树形结构,图形结构。
集合结构:除了同属于一种类型外,别无其它关系 ;
线性结构:元素之间存在一对一关系,常见类型有:数组,链表,队列,栈,它们之间在操作上有所区别。例如:链表可在任意位置插入或删除元素,而队列在队尾插入元素,队头删除元素,栈只能在栈顶进行插入,删除操作;
树形结构:元素之间存在一对多的关系,常见类型有:树(二叉树、平衡二叉树、查找树等);
图形结构:元素之间存在多对多的关系,图形结构中每个结点的前驱结点个数和后续结点个数可以任意。
物理结构又叫存储结构,分为四种,顺序存储结构、链式存储结构、索引结构、散列结构。
顺序结构:一段连续的内存空间。
优点:随机访问。缺点:插入删除效率低,大小固定。
链式结构:不连续的内存空间。
优点:大小动态扩展,插入删除效率高。缺点:不能随机访问。
索引结构:为了方便查找,整体无序,但索引块之间有序,需要额外空间,存储索引表。
优点:对顺序查找的一种改进,查找效率高。缺点:需额外空间存储索引表。
散列结构:选取某个函数,数据元素根据函数计算存储位置。可能存在多个数据元素存储在同一位置,引起地址冲突。
优点:查找基于数据本身即可找到,查找效率高。存取效率高。缺点:存取随机,不便于顺序查找。

七、const知道吗?解释其作用。

1、const 修饰类的成员变量,表示成员常量,不能被修改。
2、const 修饰函数,在本函数内部不会修改类内的数据成员,不会调用其它非 const 成员函数。
3、如果 const 构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数。
4、const 函数只能调用 const 函数。非 const 函数可以调用 const 函数。
5、类体外定义的 const 成员函数,在定义和声明处都需要 const 修饰符。

八、类的static变量在什么时候初始化?函数的static变量在什么时候初始化?

类的静态成员变量在类实例化之前就已经存在了,并且分配了内存。函数的static变量在执行此函数时进行初始化。

九、堆和栈的区别?堆和栈的生命周期?

1、堆栈空间分配区别:
栈:由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其分配方式类似于数据结构中的栈;
堆: 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,其分配方式类似于数据结构中的链表。
2、堆栈缓存方式区别:
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
3、堆栈数据结构区别:
堆:堆可以被看成是一棵树,如:堆排序;
栈:一种先进后出的数据结构。

十、解释下封装、继承和多态?

1、封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。
封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
2、继承主要实现重用代码,节省开发时间。子类可以继承父类的一些东西。
3、多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

十一、指针和引用的区别?

1、指针是一个变量,存储的是变量(对象)的地址,引用是变量的别名
2、指针可以为空,引用定义时必须初始化
3、指针在初始化之后可以改变指向,引用在初始化之后不可在改变
4、指针可以有多级,引用只有一级
5、sizeof 指针得到的是本指针的大小,sizeof 引用得到的是引用所指向变量的大小
6、当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但不是同一个变量,在函数中改变这个变量的指向不影响实参,而引用却可以
7、引用本质是一个指针,同样会占4字节内存;指针是具体变量,需要占用存储空间
8、不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针

十二、什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?

动态分配内存所开辟的空间,在使用完毕后未手动释放,导致一直占据该内存,即为内存泄漏。
方法:malloc/free要配套,对指针赋值的时候应该注意被赋值的指针是否需要释放;使用的时候记得指针的长度,防止越界;动态分配内存的指针最好不要再次赋值。

十三、重载overload,覆盖(重写)override,隐藏(重定义)overwrite,这三者之间的区别?

1、overload,将语义相近的几个函数用同一个名字表示,但是参数列表(参数的类型,个数,顺序不同)不同,这就是函数重载,返回值类型可以不同
特征:相同范围(同一个类中)、函数名字相同、参数不同、virtual关键字可有可无
2、override,派生类覆盖基类的虚函数,实现接口的重用,返回值类型必须相同
特征:不同范围(基类和派生类)、函数名字相同、参数相同、基类中必须有virtual关键字(必须是虚函数)
3、overwrite,派生类屏蔽了其同名的基类函数,返回值类型可以不同
特征:不同范围(基类和派生类)、函数名字相同、参数不同或者参数相同且无virtual关键字

十四、浅谈malloc/free和new/delete 的区别

malloc和new的区别
1、malloc是库函数,需要包头文件才能成功运行编译;new是操作符(C++中的关键字),需要在C++的环境下使用。
2、malloc既可以在C语言中使用也可以在C++中使用,new只能在C++中使用。
3、malloc传参需要自己计算空间大小(显示传参);new直接传类型,编译器自动识别类型所需空间大小。
4、malloc返回值是一个void*的指针,需要强制转换成我们所需要的类型才能进行操作;new返回的是一个对象指针,严格匹配对象类型(new是一个类型安全的操作符),可直接操作。
5、malloc失败后返回NULL,new失败后抛异常。
6、malloc需要手动初始化开辟的空间,new自动调用构造函数初始化所开辟的空间。
7、malloc不支持被重载,new可以被重载。
8、malloc所申请的内存是从堆上分配的,new所申请的空间是从自由存储区分配的,自由存储区不等于堆(取决于operator new在哪里为对象分配内存)。
9、malloc没有支持对应数组动态增容的快捷方式,new中有相应的new[]来进行数组动态增容的操作。
free和delete 的区别
1、free是库函数,需要包头文件才能成功运行编译;delete是操作符(C++中的关键字),需要在C++的环境下使用。
2、free既可以在C语言中使用也可以在C++中使用,delete只能在C++中使用。
3、free只释放malloc所开辟的内存空间,delete自动调用对象的析构函数析构对象并释放new所开辟的内存空间。
4、free不支持被重载,delete可以被重载。
5、free只能和malloc搭配使用,如果和new搭配编译不通过;delete既能跟malloc搭配使用,又能和new搭配使用,但由于和malloc搭配使用可读性太差,所以不建议使用delete搭配malloc。
为什么有了new/delete后,还要保留malloc/free?
很多C++程序里需要调用C函数,C语言中只能用malloc/free管理动态内存。

十五、虚函数、纯虚函数

虚函数:虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数,是C++中多态性的一个重要体现。利用基类指针访问派生类中的虚函数,这种情况下采用的是动态绑定技术。
纯虚函数:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”.纯虚函数不能实例化对象。

十六、结构体struct和共同体union的区别

结构体:将不同类型的数据组合成一个整体,是自定义类型
共同体:不同类型的几个变量共同占用一段内存
1)结构体中的每个成员都有自己独立的地址,它们是同时存在的;共同体中的所有成员占用同一段内存,它们不能同时存在;
2)sizeof(struct)是内存对齐后所有成员长度的总和,sizeof(union)是内存对齐后最长数据成员的长度、
结构体为什么要内存对齐呢?
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。

十七、#define和const的区别

1)#define定义的常量没有类型,所给出的是一个立即数;const定义的常量有类型名字,存放在静态区域
2)处理阶段不同,#define定义的宏变量在预处理时进行替换,可能有多个拷贝,const所定义的变量在编译时确定其值,只有一个拷贝。
3)#define定义的常量是不可以用指针去指向,const定义的常量可以用指针去指向该常量的地址
4)#define可以定义简单的函数,const不可以定义函数

十八、C++文件编译与执行的四个阶段

1)预处理:根据文件中的预处理指令来修改源文件的内容
2)编译:编译成汇编代码
3)汇编:把汇编代码翻译成目标机器指令
4)链接:链接目标代码生成可执行程序

二十、C++的内存管理

在C++中,内存被分成五个区:栈、堆、自由存储区、静态存储区、常量区
栈:存放函数的参数和局部变量,编译器自动分配和释放
堆:new关键字动态分配的内存,由程序员手动进行释放,否则程序结束后,由操作系统自动进行回收
自由存储区:由malloc分配的内存,和堆十分相似,由对应的free进行释放
全局/静态存储区:存放全局变量和静态变量
常量区:存放常量,不允许被修改

二十二、深拷贝和浅拷贝的区别

深拷贝和浅拷贝的区别:浅拷贝主要是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝需要不但对指针进行拷贝,并对指针指向的内容进行拷贝,经过深拷贝后的指针是指向两个不同地址的指针。

二十三、友元函数和友元类

1、类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。声明友元函数的语句可以放在类中任意位置。
2、若声明B类是A类的友员类,即A类中声明B类是自己的好朋友,则B类的所有成员函数都是A类的友员函数,A类中的所有成员都像好朋友B开放。友员类通常设计为一种对数据操作或类之间传递消息的辅助类。

二十四、C++中的基本数据类型及派生类型

1)整型 int
2)浮点型 单精度float,双精度double
3)字符型 char
4)逻辑型 bool
5)控制型 void
基本类型的字长及其取值范围可以放大和缩小,改变后的类型就叫做基本类型的派生类型。派生类型声明符由基本类型关键字char、int、float、double前面加上类型修饰符组成。

二十五、栈溢出的原因以及解决方法

栈溢出是指函数中的局部变量造成的溢出(注:函数中形参和函数中的局部变量存放在栈上)
栈的大小通常是1M-2M,所以栈溢出包含两种情况,一是分配的的大小超过栈的最大值,二是分配的大小没有超过最大值,但是接收的buf比原buf小。
1)函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈
2)局部变量体积太大。
解决办法大致说来也有两种:
1> 增加栈内存的数目;如果是不超过栈大小但是分配值小的,就增大分配的大小
2> 使用堆内存;具体实现由很多种方法可以直接把数组定义改成指针,然后动态申请内存;也可以把局部变量变成全局变量,一个偷懒的办法是直接在定义前边加个static,呵呵,直接变成静态变量(实质就是全局变量)

二十六、什么是野指针

野指针不是NULL指针,是未初始化或者未清零的指针,它指向的内存地址不是程序员所期望的,可能指向了受限的内存。
成因:
1)指针变量没有被初始化
2)指针指向的内存被释放了,但是指针没有置NULL
3)指针超过了变量了的作用范围,比如b[10],指针b+11

二十七、引用作为函数参数以及返回值的好处

对比值传递,引用传参的好处:
1)在函数内部可以对此参数进行修改
2)提高函数调用和运行的效率(所以没有了传值和生成副本的时间和空间消耗)
值传递:
形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。
指针传递:
形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作
引用传递:
形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。用引用作为返回值最大的好处就是在内存中不产生被返回值的副本。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值