c++面试整理系列之1

1.strcpy()

char * strcpy( char *strDest, const char *strSrc ) 
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest; 
 while( (*strDest++ = * strSrc++) != ‘\0); 
 return address;
}

2.下列代码有何问题

void GetMemory( char **p, int num )
{
 *p = (char *) malloc( num );
}
void Test( void )
{
 char *str = NULL;
 GetMemory( &str, 100 );
 strcpy( str, "hello" ); 
 printf( str ); 
}
1.*p没有判断NULL 的情况
2.没有free
3.printf没有字符输出格式化
void Test( void )
{
 char *str = (char *) malloc( 100 );
 strcpy( str, "hello" );
 free( str ); 
 ... //省略的其它语句
}
1.没有判断*str=NULL
2.*str释放以后没有等于NULL 

3.编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefg” 函数头是这样的:
//pStr是指向以’\0’结尾的字符串的指针
//steps是要求移动的n

void LoopMove(char *str,int steps){
	int len = strlen(str);
	char tmp[MAXSIZE];
	strcpy(tmp,str+len-steps);
	strcpy(tmp+steps,str);
	*(tmp+len) = '/0';
	strcpy(str,tmp);
}
void LoopMove(char *str,int steps){
	int len = strlen(str);
	char tmp[MAXSIZE];
	memcpy(tmp,str+len-steps,steps);
	memcpy(str+steps,str,len-steps);
	memcpy(str,tmp,steps);
}

4.编写string 的构造函数,析构函数和赋值函数

class String{
public:
	String(const char *str = NULL);//普通构造函数
	String(const String &other);//拷贝构造函数
	~String(void);//析构函数
	String &operator =(const String &other);//赋值函数
private:
	char *m_data;//用于保存字符串
}
//普通构造函数
String::String(const char *str){
	if(str == NULL){
		m_data = new char[1];
		*m_data = '\0';
	}else{
		int length = strlen(str);
		m_data = new char[length+1];
		strcpy(m_data,str);
	}
}
//普通析构函数
String::~String(void){
 delete [] m_data;
}
//拷贝构造函数
String::String(const String &other){
	int length = strlen(other.m_data);
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);
}
//赋值函数
String &String::operator =(const String &other){
	if(this == &other)
		return *this;
	delete [] m_data;
	int length = strlen(other.m_data);
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);
	return *this;
}

5.static和const的作用

  • Static

(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2) 在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

  • const

(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了; >(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的 成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”

6.怎么用一个函数,判断大端还是小段纳,如果是小端则返回1,如果是大端则返回0

#include <iostream>
int checkCPU(){
union w{
	int a;
	char b;
} c;
c.a = 1;
return(c.b == 1);
}

7.C和C++的区别

  • 思想上:C++是面向对象的语言,而c是面向过程的结构化编程语言。
  • 语法上:C++有封装,继承和多态三种特性
    C++相比C,增加许多安全功能,比如强制类型转换
    C++支持范式编程,比如模板类,函数模板等

8.c++中四种cast转换

  • const_cast:用于将const变量转换为非const变量
  • static_cast:用于各种隐式转换,比如非const转const,void*转指针等,static_cast能用于多态向上转换但是不能向下转换
  • dynamic_cast:动态类型转换。只能用于含有虚函数的类,用于类层次之间转换,只能够转指针或者引用,非法指针返回NULL,非法引用抛出异常
  • reinterpret_cast:什么都可以转换

9.指针和引用的区别

  • 指针拥有独立的存储空间,引用共享存储空间,引用只是一个别名
  • 指针可以初始化为NULL,而引用则是被引用对象的大小
  • sizeof()引用对象的大小,而指针则是地址4/8
  • 可以有const指针但是没有const引用
  • 指针可以有多级指针,而引用只能够是一级
  • 指针++和引用++含义不一样
  • 指针可以指向其他对象,而引用则不能够在改变
  • 作为传递参数的时候,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象
  • 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。

10.c++中四个智能指针的理解:shared_ptr,unique_ptr,weak_ptr,auto_ptr

为什么要使用智能指针:智能指针的作用是管理一个指针,因为有些时候申请的内存空间忘记释放,造成内存泄露。使用智能指针能够很大的避免这类事情的发生,因为智能指针就是一个类,当超出作用域的时候类会自动调用析构函数,自动释放内存空间,不需要手动释放空间

 - auto_ptr:采用所有权模式
#include <iostream>
unique_ptr<string> p1(new string("auto"));
unique_ptr<string> p2;
p2 = p1;//不会报错
//p2剥夺p1的运行权,当访问p1的时候会报错,auto_ptr的缺点是存在潜在崩溃 的情况

  • unique_ptr:保证同一时间内只有一个智能指针可以指向该对象它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。
#include <iostream>
unique_ptr<string> p1(new string("auto"));
unique_ptr<string> p2;
p2 = p1;//禁止如果强制需要的话就用 p2 = move(p1);
p2 = unique_ptr<string> new string("I love you");
  • shared_ptr:实现共享式拥有的概念。多个指针可以指向相同的对象,该对象和相关资源会在最后一个引用被销毁时释放。它使用计数机制计算同一个资源被几个指针共享,通过use_count()来查看资源所有者的个数,处了通过new来构造,还可以通过传入auto_ptr,unique_ptr,weak_ptr来构造。当我们调用release时,当前指针会释放资源所有权:
成员函数:
	use_count();//返回计数的个数
	swap();//交换两个shared_ptr对象(交换所拥有的对象)
	reset();//放弃内部对象的所有权或拥有对象的变更,计数减少
	get();//返回内部指针 如:shared_ptr<int>sp(new int(1))此时sp等价sp.get()
  • weak_ptr:不控制生命周期的智能指针,他指向一个shared_ptr管理的对象,进行该对象的的内存管理的是哪个强引用的shared_ptr.weak_ptr只是对象的一个访问对手段。weak_ptr设计的目的是为了配合shared_ptr而引用的一种智能指针来协助shared_ptr工作,它只可以从一个shared_ptr或者另外一个weak_ptr中构造。它的构造和析构不会引起引用计数的增加或者减少。weak_ptr是用来解决shared_ptr相互引用的死锁的问题,如果说两个shared_ptr之间可以相互引用,那么这两个指针的引用计数永远不可能下降为0永远不会释放。它是对对象的一种弱引用,不会增加对象的引用数,和shared_ptr之间相互转换,shared_ptr可以直接赋值给他,它可以通过调用lock函数来获得shared_ptr
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete\n";
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}

11.数组和指针的区别

指针 数组
保存数据的地址 保存数据的具体值
通常用于动态数据 结构 通常用于固定数据数目和数据类型
通过malloc分配内存 隐式分配和删除
访问数据,首先获得指针的内容通过地址访问数据,效率更低 直接访问数据

12.什么是野指针
野指针就是指向一个已经删除的对象或者未申请访问受限区域的指针。

13.为什么析构函数必须是虚函数?为什么c++默认的析构函数不是虚函数?

  • 将可能会被继承的父类析构函数设置成为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该对象,释放基类指针时可以释放掉子类空间防止内存泄露。
  • c++默认析构函数不是虚函数因为虚函数需要而额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数则会浪费内存。因此c++默认的析构函数不是虚函数,而是只有当需要作为父类的时候才需要。

14.函数指针,指针函数

  1. 定义
    函数指针是指向函数的指针变量
    指针函数是,函数的返回值是指针
  2. 用途
    函数指针:回调函数
  3. case
char *fun(char *p){...}//函数fun
char *(*pf)(char *p);//函数指针pf
pf = fun ;//函数指针指向fun
pf(p);//通过函数指针调用函数fun

15.map和set的区别

map和set都是c++的关联容器,其底层实现都是红黑树(RB-Tree).由于map和set所开放的各种操作接口,RB-tree也都提供了,所以几乎所有的map和set的操作行为都是转调RB_tree的操作行为
(1)map的元素是key-value(关键字-值)对:关键字起到索引的作用,set与之相对的就是关键字的简单的集合,set中的每个元素只包含一个关键字
(2)set的迭代器是const的,不允许修改元素的值,map允许修改value,但不允许修改key。其原因是因为map和set是根据关键字排序来保证其有序性的,如果修改key的话,那么首先要删除该键,然后调节平衡,在插入修改后的键值,调节平衡,如此以来严重破坏了map和set的结构,导致iterator失效,不知道应该指向改变前的位置,还是指向改变后的位置,所以将STL中将set的迭代器设置成const,不允许修改迭代器的值;而map中的迭代器不允许修改key的值
(3)map具有支持下表的操作,set不支持下标的操作,map可以用key作为下标,map的下标运算符[]将关键码作为下标去执行查找,如果关键码不存在则插入。

16.介绍一下STL的allocaotr

STL的分配器用于封装STL容器在内存管理上的细节。在C++中,其内存配置和释放如下: new运算分两个阶段:(1)调用::operator
new配置内存(2)调用对象构造函数构造对象内容 delete运算分两个部分:(1)调用对象析构函数 (2)调用::operator
delete释放函数 为了精密分工,STL
allocator将两个阶段操作区分开来:内存配置有alloc::allocate()负责;对象构造由::construct()负责,对象析构由::destroy负责。
同时为了提升内存管理的效率,减少申请内存造成的内存碎片,SGI
STL采用了两级配置器,当分配的空间大小超过128g时采用第一级,malloc().realloc(),free()函数进行内存空间的分配和释放,而第二级空间配置器采用内存池子的技术,通过空闲链表管理内存

17.Struct和classs的区别

在C++中,可以用struct和class定义类,都可以继承。区别在于:structural的默认继承权限和默认访问权限是public,而class的默认继承权限和默认访问权限是private。
另外,class还可以定义模板类形参,比如template <class T, int i>。

18.如何判断是否内存泄露

  • 使用linux下面的内存泄露检查工具Valgrind.另外一方面在写代码时候可以添加呢村申请和释放的统计功能,统计当前申请和释放内存是否一致,以此来判断是否泄露

19.为什么会发生段错呢?

段错误通常发生在访问非法内存地址的时候:
1.使用野指针
2.试图修改字符串常量的内容

20.malloc的原理,另外调用brk系统调用和mmap系统调用的作用分别是什么?
Malloc函数用于动态分布内存。为了减少内存碎片和系统调用的开销,malloc采用内存池的方式,先申请大块内存作为堆区,然后将堆区分为多个小块,以快为单位作为内存管理的基本单位。当用户申请内存时,直接从堆分配一块合适的空闲块。Malloc采用隐式链表结构将堆区分成连续的,大小不一的快,包含分配块和未分配快,同时malloc采用显式链表结构来管理所有的空闲块,即使用一个双向链表将空闲块连接起来,每一个块记录了一个连续的,未分配的地址。
当进行内存分配的时候,malloc会通过隐式链表遍历所有的空闲块,选择满足要求的块进行分配,当进行内存合并的时候,malloc会使用边界标记法,根据每个块的前后快是否已经分配来决定是否进行块合并。
采用Malloc在进行申请内存的时候,一般会通过brk或者mmap系统进行调用进行申请。其中当申请的内存少于128K时,会使用系统函数brk在堆中进行分配;当时申请内存大于128K时会使用系统函数mmap在映射区分配。
21.c++中内存管理什么样子的

在C++中,虚拟内存分为代码段,数据段,BSS,堆区,文件映射区,栈区。
代码段:包括只读存储区和文本存储区,其中只读存储区存储字符串常量,文本存储区存储程序的机器代码
数据段:存储已经初始化的全局变量和静态变量
bss段:存储未初始化的全局变量和静态变量(局部+全局),以及所有被初始化为0的全局变量和静态变量
堆区:调用new/malloc函数时在堆区动态分配内存,同时需要delete和free手动释放内存
栈:函数参数局部变量,linux创建进程用ulimit设置
映射区域:动态连接库以及调用mmap函数进行文件映射

22.解释一下内存泄漏

  • 概念:内存泄露指的是申请的内存不再使用但是没有释放。
  • 堆内存的泄漏
  • 系统资源泄漏:主要指程序使用系统分配的资源比如Bitmap,handle,SOCKET等没有使用相应的函数释放掉,导致系统资源的严重浪费,严重可导致系统效能降低,系统运行不稳定。
  • 没有将基类的析构函数设置成为虚函数。继承的子类,没有调用析构函数因此没有进行释放

23.New和Malloc的区别
1.new是一个操作符不可以重载,malloc是一个库函数
2.new异常抛出bad_malloc,malloc抛出NULL
3.malloc分配要按照指定的大小,而new分配则是指定类型
4.new返回的是指定类型的指,而malloc返回的则是void*,因此malloc一般需要进行类型转化。

24.c++11的新特性

1)关键字以及新语法:auto,nullptr,for
2)STL容器:std::array,std::forward_list,std:unordered_map,std::unordered_set
3)多线程:std::thread,std::atomic,std::consition_variable
4) 智能指针内存管理:std::shared_ptr,std::weak_ptr
5) 其他:lamda表达式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章