C/C++面试题
关键字
1."#","##"的用法
#是字符串转换符,##是字符串连接符;发生在预处理阶段;
2.volatile的含义
防止编译器优化,告诉编译器每次都去真实地址中读取,而不是从寄存器或者缓存中;
多线程修改的共享变量,中断修改的全局变量,硬件寄存器访问
3.static的作用
static修改变量的作用域和生命周期;
static修饰全局变量,限制它的作用域在本文件;
static修饰局部变量,会将它的周期变为全局生命周期;
static修饰类成员变量,该变量变成类的属性;
static修饰类成员函数,该函数变成全部对象共享的函数,不用实例化就可以使用,且只能修改静态成员变量;
4.为什么static只初始化一次?
因为static变量具有全局的生命周期,初始化之后不会被销毁,所以不用再次初始化;
5.extern C 的作用是什么?
提示编译器这部分按照c语言去编译;
6.const有什么作用?
const代表我们的常量,修饰之后不可修改,且必须初始化
7.什么时候用const?
可以修饰一般常量,常数组,常对象,常指针,常引用,函数常参数,函数返回值;
指针常量:int*const p;p不变,指向不变
常量指针:int const*p;const int*p;*p不变,指向的值不变
指向常量的常量指针:const int* const p;
常量有可能存在:只读数据段,堆(动态分配),栈(局部常量),静态区(静态常量);
8.new/malloc/delete/free的区别?
①new和delete不需要自己计算大小,malloc和free需要
②new和delete会调用构造和析构,但malloc只是单纯的分配和释放内存
③new和delete会返回对应类型的指针,malloc和free返回void*;
new = malloc + 构造函数 + 强制类型转换;
9.strlen和sizeof的区别?
①库函数和关键字
②运行期和编译期
③计算字符串的长度,计算操作数的长度;strlen("\0") =0,sizeof("\0")=2。
10.不使用size of,如何求int占用的字节数?
int *p;
#define mysizeofint(value) (char*) &(p+1)-(char*) &p
11.struct 和 union的区别?
struct是为所有成员顺序分配空间,union是所有成员共享一片空间,空间大小等于最大的元素,每次只能有一个变量生效;
假设32位的机器
typedef union {double i; int k[5]; char c;}DATE; 大小:8,20,1 = 20——》24(8字节对齐)
typedef struct data( int cat; DATE cow;double dog;)too;4+24+(4)+8 = 40
12.struct的对齐规则?为什么要对齐?
内部对齐:变量的起始地址必须是变量大小的整数倍;
外部对齐:结构体大小必须是最大的基础类型的整数倍;
对齐可以提高访问的速度;
12.左值和右值是什么?
左值和右值的区别在于有没有确定的地址;左值可写右值可读;
13.短路求值?
布尔表达式中,第一个表达式如果确定了表达式的结果,将不会计算第二个表达式;
14.++i和i++有什么区别?是怎么实现的?
++i复制到了临时的存储空间,temp = i;temp += 1;return temp;
i++是直接加1;i +=1;return i;
内存
1.内存的分配方式?
堆上分配,栈上分配,静态区分配(在编译之前完成);
2.堆栈的区别?
①堆是由用户自己申请和释放的;栈是操作系统自动申请和释放的
②堆是不连续的,用链表来进行管理;栈是连续的,占用内存小;生长方向也不同;
③栈的效率比堆更高;
3.栈的作用?
栈可以用来存局部变量,函数参数,返回地址,寄存器值等;一般用于函数调用,多线程切换,中断和异常时保护现场和恢复线程;
4.函数参数压栈顺序?
ARM用R0~R3来传递函数参数,如果超过了4个,那么将用栈来存储参数,从右至左,最后一个最先入栈,第一个参数最后入栈,更容易的实现动态变化的参数个数,不需要知道参数的个数;
5.c++如何处理返回值?
按值返回,按常量引用返回;
6.c++的内存管理
代码段,bss段,data段,堆,文件映射区,栈;
7.什么是内存泄漏?
申请了内存,使用完没有释放;
申请了一片内存,没有指针指向它,那它就泄漏了;(未释放,不匹配,虚析构,循环引用)
8.如何防止内存泄漏?
①良好的编程习惯,申请和释放成对出现;
②使用智能指针;
③使用STL库,它们会自动进行内存管理;
④RALL思想,超出作用域自动释放;
9.如何检测内存泄漏
①日志:申请和释放都打印指针地址;
②统计:申请和释放都统计次数;
③valgrind:valgrind --leak-check=full ./leak 分析代码,解决问题;
指针
数组指针和指针数组的区别?
数组指针是指指向数组的指针;输出:5
#include <stdio. h>
#include <stdlib. h>
void main()
{
int b[12]={1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4];
p = b;
printf("%d\n", **(++p);
}
指针数组是指数组存储的都是指针;输出:1234
#include <stdio.h>
int main()
{
int i;
int *p[4];
int a[4]={1,2,3,4};
p[0] = &a[0];
p[1] = &a[1];
p[2] = &a[2];
p[3] = &a[3];
for(i=0;i<4;i++)
printf("%d",*p[i]);
printf("\n");
return 0;
}
函数指针和指针函数有什么区别?
函数指针是指一个指向函数地址的指针;
int(*p)(int, int); //定义一个函数指针
int a, b;
p = Max; //把函数Max赋给指针变量p, 使p指向Max函数
c = (*p)(a, b); //通过函数指针调用Max函数
指针函数表示函数返回值是一个指针;
int *pfun(int, int);
数组名和指针的区别是什么?
数组名是数组第一个元素的地址,内存偏移量是元素的大小,通过下标,数组名+元素偏移量访问;
指针保存的是地址,内存偏移量是4字节,是间接访问需要解引用*;
指针和引用的区别是什么?
①指针是实体,引用是别名;
②指针可以为空,引用不能为空;
③指针可以改变指向,引用只能初始化一次;
④自增,++指针是指向下一个地址,引用是变量加1;
⑤sizeof,指针是4字节,引用是变量大小
野指针是什么?
野指针指的是指向不可用内存的指针;当指针创建的时候,默认值是随机的;当指针free掉的时候,只free了指向的内存,指针本身没有释放,得指向null;
如何避免野指针?
①指针初始化;
②指针用完之后释放内存,将指针指向NULL;
③使用智能指针;
智能指针
C++11使用三种智能指针来帮助动态管理内存,
unique:独享对象的指针所有权
shared:共享对象的指针所有权,引用计数,拷贝的时候引用计数+1,释放的时候-1,当为0,就释放对象;
weak:解决循环引用的问题;
智能指针的内存泄漏如何解决?
weak_ptr不会影响引用计数,即使存在循环引用,引用计数也可以降到0,从而正确释放对象;
预处理
typedef和define有啥区别?
①typedef是关键字,有类型检查的功能;define是预处理指令,没有类型检查的功能,只是简单的文本替换;
②typedef是为变量类型起别名,但define不仅可以为变量类型起别名还可以定义常量,开关等
③对指针的操作不同:
#define INTPTR1 int*
typedef int* INTPTR2;
INTPTR1 pl, p2;//p1是指针,p2是int
INTPTR2 p3, p4;//p3,p4都是指针
如何使用define声明常数?
#define SECOND_PER_YEAR (60*60*24*365UL)
#include<>和#include""的区别?
<>从标准库的路径查找,系统文件比较快;""从用户工作路径开始找,适用自定义文件;
头文件的作用?
①封装保护:通过头文件来调用库函数;
②头文件能加强类型安全检查;
在头文件中定义静态变量和全局变量可行吗?
不可以,这样会使得每个包含头文件的cpp文件都会单独存在一个静态/全局变量,引起空间浪费和链接错误;
写一个标准宏,MIN?
#define MIN(A,B) ((A)<=(B)?(A):(B))
不使用流程控制语句,如何打印1-1000?
#include <stdio.h>
#define A(x) x;x;x;x;x;x;x;x;x;x;
int main(){
int n = 1;
A(A(A(printf(%d,n++))));
return 0;
}
变量
全局变量和局部变量的区别?
①生命周期
②作用域
③存储位置
全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
不建议;一般我们是在c文件中声明全局变量,然后.h extern全局变量;
局部变量能否和全局变量重名?
面向对象
面向对象和面向过程的区别?
面向对象:将对象作为程序的基本单位,对象包含了数据和操作数据的函数,更能模拟客观世界,拥有更好的数据封装,代码的灵活性和复用性也更高;
面向过程:将程序看出一系列命令的集合,一组函数的顺序执行;
面向对象的基本特征?
抽象:将数据或过程抽象成一个实体;
继承:鼓励类的重用,表述共性;
封装:限制对数据的访问只能通过开放的接口;
多态:不同类对象对相同的消息做出的不同响应;
strcut和class的区别是什么?
struct的默认继承和访问权限全是public,class有不同的继承和访问权限,public,protect,private;
C++中类成员的访问权限?
在类内,无论是public,protect,private都可以访问;在类外,只能访问public的;
C++空类默认产生哪些类成员函数?
class Empty
{
public:
Empty(); // 构造函数
Empty( const Empty& ); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
int main() {
Empty a; // 调用构造函数
Empty b(a); // 调用拷贝构造函数
Empty* ptr = &b; // 隐式调用取址运算符
Empty c;
c = a; // 调用赋值运算符
const Empty* constPtr = &c; // 隐式调用 const 取址运算符
return 0;
}
拷贝赋值函数的形参能否进行值传递?
可以,但会调用多一次拷贝构造,并不会无限递归;产生额外的赋值;
构造函数没有返回值,怎么知道对象是否构造成功?
如果构造失败,构造函数会抛出异常;如果已经发生了部分构造,已经完成构造的子对象将会逆序被析构;
初始化列表和构造函数初始化的区别?
初始化列表是直接初始化类的成员,构造函数初始化是对类的成员进行赋值;
哪些情况智能初始化列表,不能用赋值?
①当类中含有const和&引用时,只能初始化,不能对它赋值;
②当类成员是没有默认构造函数的类
③当基类没有默认构造函数,
析构函数作用是什么?
完成对象删除之前的清理工作;
虚函数是什么?
虚函数是一个可以在派生类重写的函数,主要用于实现多态;通过基类指针,指向不同的对象就可以调用相应的虚函数;
C++如何实现动态多态?虚函数表是如何实现运行时多态的?
每个对象内部都有一个虚函数表指针,指向本类的虚函数表的内存地址;
每个类的虚函数表会将函数地址按顺序存入,当子类重写父类虚函数的时候,虚函数对应位置的函数会被子类重写的虚函数覆盖;
在基类指针指向派生类的时候,虚函数表指针会表面具体调用的函数是哪个;
重载(静态多态)和覆盖(动态多态)有啥区别?
①覆盖是父类子类之间的关系,重载是同一个类的关系;
②覆盖是一对方法之间的关系,重载是多个方法之间的关系;
③覆盖是由对象来决定的,重载是由调用的形参和实参;
纯虚函数指的是什么?
纯虚函数指的是没有办法在基类给出的虚函数有意义的实现,如动物;
但在派生类中必须实现,否则无法实例化;如老虎
拥有纯虚函数的类叫抽象类,抽象类不能生成对象;
静态函数和虚函数的区别?
静态函数在编译期就确定了,但虚函数是在运行时动态绑定的。虚函数用虚函数表机制,调用的时候会增加一次内存开销;
C++如何阻止一个类被实例化?
使用抽象类或将构造函数声明为private;
什么函数不能声明为虚函数?
①普通函数;②构造函数;③内联成员函数;④静态成员函数;⑤友元函数
为什么析构函数必须是虚函数?为什么默认不是?
当一个类需要作为父类的时候,析构函数就需要声明为虚函数,这样可以使得基类指针释放的时候,也可以析构掉子类空间,防止内存泄漏;
因为不是所有类都会成为父类,虚函数需要虚函数表指针和虚函数表,是会占用空间的,没有继承关系的类,其实不用浪费这个资源;
析构函数可以virtual,构造函数不能,为什么?
虚函数是为了继承产生多态,运行时根据对象类型绑定函数;析构函数运行之前对象还没建立,不存在动态绑定一说;
类成员变量的初始化顺序?
①基类静态成员变量(类外)
②派生类静态成员变量 (类外)
③初始化列表:初始化顺序跟定义成员变量的顺序有关 / 构造函数:与构造函数中的位置有关;
类的内存分布?
当一个类为另一个类的成员变量时,如何对其初始化?
class ABC
{
public:
ABC(int x, int y, int z);
private :
int a;
int b;
int c;
};
class MyClass
{
public:
MyClass():abc(1,2,3)
{
}
private:
ABC abc;
};
C++类内可以定义引用数据成员吗?
可以,但必须要用初始化列表初始化;
什么是右值引用,什么是左值引用?
左值能取地址,右值不能取地址;
左值引用指向具有标识符的对象,用于传统的引用操作;
右值引用在移动语义和完美转发场景;
std::vector<int> source = {1, 2, 3, 4, 5};
// 使用移动语义将source向量的内容移动到destination向量
std::vector<int> destination = std::move(source);
//std::move(source)将source的内容移动到destination,避免了不必要的拷贝操作;
//现在source={},destination = {1, 2, 3, 4, 5}
// 函数模板,完美转发参数到另一个函数
template <typename T>
void forwarder(T&& arg) {
receiver(std::forward<T>(arg));
}
// 接受转发参数的函数
void receiver(int&& x) {
std::cout << "Received rvalue: " << x << std::endl;
}
//左值不会
void receiver(const int& x) {
std::cout << "Received lvalue: " << x << std::endl;
}
什么是深拷贝?什么是浅拷贝?
浅拷贝只拷贝指针,指向同一片内存;
深拷贝不仅拷贝指针,内存也拷贝了一份,指针指向各自的内存;
Public继承、protected继承、private继承的区别?
Public:派生类所有成员继承了基类的访问权限;
Protect:基类的公有成员在派生类中都变为保护成员;
Private:共有成员和保护成员都作为派生类的私有成员;
基类的构造函/析构函数数能否被派生类继承?
不会被继承;派生类会调用基类的构造和析构函数,但不是继承;
什么是友元?
友元提供了一种普通函数访问类的保护和私有成员的机制;
友元函数:普通函数可以访问B类的私有/保护成员
友元类:A类的类成员函数可以访问B类私有/保护成员;
C++能设计不能被继承的类吗?
{
public :
static FinalClass1* GetInstance()
{
return new FinalClass1;
}
static void DeleteInstance( FinalClass1* pInstance)
{
delete pInstance;
pInstance = 0;
}
private :
FinalClass1() {}
~FinalClass1() {}
};
将构造函数和析构函数变成私有成员,再写一个静态创建类和释放类的函数;
位处理,容器和算法,数据结构待补充