【C++方向】 面试问题汇总


前言

包含本人面试遇到的问题和面经里面的问题。好记忆不如烂笔头
一些资料链接:
xmind导图:https://pan.baidu.com/s/1cVSlZ6s-sM4ny1TZL6RoSQ 提取码:f97q
 
 
 

C++


问题描述】谈一谈static关键字
】static关键字可以修饰局部变量、全局变量、函数、类中的成员变量和函数

  • 修饰局部变量
       一般情况下局部变量都是在栈区被定义,并且在语句块结束的时候,局部变量会被系统自动回收。如果使用static修饰局部变量,该变量就存放在静态数据区,其生命周期可以持续到程序执行结束。但是,局部变量的作用域不会发生变化,仍然在定义该局部变量的语句块中。
  • 修饰全局变量
       对于一个全局变量,它既可以在本源文件中被访问,也可以在同一个工程的其他源文件中被访问(只需要用extern声明)。但是,用static修饰过的全局变量只用在本源文件中使用,即作用域从整个工程下降到定义该全局变量的源文件中
      比如下面两个源文件属于一个工程。如果加static,则连接的时候会出错
// A.cpp
#include "pch.h"

//int a = 10;
static int a = 10;
//B.cpp
#include "pch.h"
#include <iostream>
using namespace std;

extern int a;

int main()
{
	cout << a << endl;
}
  • 修饰函数
      和修饰全局变量一致
  • 在类中使用static
      在类定义中对某个函数或者变量使用static,则表示该函数或者变量属于这个类而不是类的某个实例化对象。使用static修饰之后,函数或者变量在存储空间中都只存在一个副本。可以通过类名和对象调用。
class A{
public:
	static int a;  //不能写static int a = 1;
	A(){}
	~A(){}
};

int A::a = 1;  //初始化方法

 

问题描述】谈一谈const关键字
】const常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。

  • 修饰一般变量和数组
      可以放在类型说明符前,或者说明符之后,结果一样。
	const int a = 0;
	int const b = 1;
	
	const int t[10] = {0,1,2,3,4};
	int const v[10] = {0,1,2,3,4};
  • 修饰指针变量*及引用变量&
      技巧:const对它左边第一个符号起作用,如果const在语句的最左边,则对右边的第一个运算符起作用。
	int a = 3;
	int b = 5;
	
//常量指针
//const和*谁在前先读谁
	const int *p1 = &a;   //常量指针。不可以通过指针修改指针所指向的内容
	//(而不是“p1所指向的内容不可以修改”),但是指针本身的指向可以修改。
	//与int const *p1 = &a等价
	
	//*p1 = 5;  //[Error] assignment of read-only location '* p1'
				//       指向的地方是一个只读的(read-only) 
	a = 10;   //OK!
	p1 = &b;
	cout<<a<<" "<<*p1<<endl;   //10 5

//指针常量
	int * const p2 = &a;   //指针常量。不可以修改p2的指向,但是可以修改p2所指向的值。
	*p2 = 20;
	cout<<*p2<<endl;   //20
	//p2 = &b;   //[Error] assignment of read-only variable 'p2'  
				//         p2是一个只读read-only) 的变量 

//常量引用
//可以这样理解下面的语句:直接使用int定义变量,变量是”可读可写”的,但是使用const int定
//以的变量则是“可读不可写”的。而且引用相当于变量的别名,和原始变量名是等价的。所以从
//const int到int相当于在原来的权限基础上生成了更大的权限,这是不允许的,而从int到
//const int相当于在原来权限的子集。
	const int v = 25;
	int& v1 = v;  //[Error] invalid initialization of reference of type 
				  //        'int&' from expression of type 'const int'

	int t = 25;
	const int& t1 = t;
	cout<<t1<<endl;  //25
	t = 28;
	cout<<t1<<endl;  //28
	t1 = 30;  //[Error] assignment of read-only reference 't1'
  • 使用const修饰函数
    • 修饰参数
      修饰参数主要是用于参数是指针或者引用。这样可以保护指针所指向的内容,以为无法通过常量指针或常量引用修改值。
void func()(const A* a)
void fund()(const A& a)
  • 修饰返回值
    修饰对象本身的情况:用于二目操作符重载函数产生新对象的时候。因为常量不可以作为赋值运算符的左值
#include <iostream>
using namespace std;

class A {
public:
	int real;
	int image;
	A(int r, int i) : real(r), image(i) {
		cout << "normal constructor" << endl;
	}
	A() {
		cout << "default constructor" << endl;
	}
	~A() {
		cout << "delete A" << endl;
	}
	A add(const A& b) {
		return A(real + b.real, image + b.image);
	}
	const A operator+(const A& b) {
		return A(real + b.real, image + b.image);
	}
};

int main()
{
	A a(1, 0), b(1, 3);
	A c = a + b;   //  合法
	// (a + b) = c;  //没有与这种操作匹配的“=”运算符,操作数类型为: const A = A

	//const A d(5, 8);
	//(a + b) = d;   //没有找到接受“const A”类型的左操作数的运算符(或没有可接受的转换)
}

const放在函数后面(注意:此函数必须是类的成员函数,不可以是普通函数),表示这个成员函数不可以修改类中的任意变量,只可以访问。与不加const的成员函数是重载关系。

  • const在类中的用法
    在类中定义常量类型的方法:

  • 使用枚举类型

class test{
	enum { SIZE1 = 10, SIZE2 = 20};
	int array1[SIZE1];
	int array2[SIZE2];
};
  • 使用const
      不能在类声明中使用常量
class test {
public:
	const int SIZE = 10;  //这句话是可以的,应该是编译器的问题
	int array[SIZE];  //error
};
 class A
 {A(int size);      // 构造函数
        const int SIZE ; 
 };
 A::A(int size) : SIZE(size)    // 构造函数的初始化表
{}
//error 赋值的方式是不行的
A::A(int size)
{
     SIZE=size;
}
  • const修饰类对象,定义常量对象
    常量对象只能调用常量函数,别的成员函数都不能调用。常量函数也只能调用其他常量函数,不可以调用非常量成员函数。
     

问题描述虚函数

 

操作系统


问题描述】说明库函数和系统调用的区别和联系。
】库函数是语言或应用程序的一部分,运行在用户空间中。而系统调用时操作系统的一部分,是内核提供给用户的程序接口,运行在内核空间中,而且许多库函数都会使用系统调用来实现功能。没有使用系统调用的库函数,执行效率通常比系统调用高。因为使用系统调用时,需要上下文的切换以及状态的转移(由用户态转向核心态)。
 

问题描述】谈一谈中断机制
】(自己理解的)中断是指某些事件使得处理器暂停当前正在处理的进程,保存现场后,转而去执行其他程序。引起中断的事件可以分为中断(外中断)异常(内中断)系统调用。当中断发生时,运行用户态的CPU会立即进入核心态,这是通过硬件实现的

  • 中断(外中断):指来自CPU执行指令以外的事件的发生,如设备发出I/O结束中断,时钟中断等。这些中断起到通知CPU的作用。
  • 异常(内中断):指源自CPU执行指令内部的事件,如算术溢出,地址越界,虚存缺页等。异常不可以被屏蔽,一旦出现应当立即处理。
  • 系统调用:指用户在程序中调用操作系统所提供的一些子功能,CPU只能在核心态下执行这些子功能。系统调用不是中断,但是它会引起中断(用户程序使用访管指令,系统根据访管指令的操作数执行对应的中断处理程序。)。
     

问题描述】进程与线程的异同
进程是程序在某个数据集上的一次动态执行。在多道程序设计中,为了更好的描述和控制程序的运行,解决程序的异步性,增加系统的并发程度,于是引入了进程的概念。线程可以理解为“轻量级进程”,它基本上不包含系统资源。线程的引入加快了系统的并行速度,因为线程的上下文切换更快速。未引入线程之前,进程是资源分配和处理器调度的基本单位,引入线程之后,进程只是用来分配资源,线程变成了处理器调度单位。 这样一来,系统的上下文切换速度变快了,而且线程之间的通信变得更加简单。
 

问题描述】谈谈进程间的通信方式
】有三种方式:共享存储,消息传递,管道通信

  • 共享存储:在通信的进程之间设置一块可直接访问的共享空间,通过对这片空间的读写操作进行进程间的通信。注意:这种方法需要同步互斥工具。
  • 消息传递:进程间的通信以格式化的消息为单位。进程通过系统提供的发送消息原语和接收消息原语进行数据交换。
  • 管道通信:“管道”是用来连接一个读进程和一个写进程的共享文件,这个共享文件是一个具有固定大小的缓冲区,它不属于文件系统,只存在于内存上。缓冲区只允许一边进行写入,一边进行读出。管道可以实现双向的数据传输,但是同一时刻只能有一个方向,所以管道的通信必然是半双工通信
     

问题描述】什么是管程?管程由那几部分组成?为什么要引入管程
】一个管程定义了一个数据结构和能被并发进程所执行的、在该数据结构上的一组操作,这组操作能同步进程和改变管程中的数据。管程包括:局部于管程的共享变量说明;对该数据结构进行操作的一组进程;对局部于管程的数据设置初始值的语句。管程的引入是为了解决临界区分散所带来的管理和控制问题。管程一次只允许一个进程进入管程内,从而既便于系统管理共享资源,又能保证互斥。
 

问题描述】形成死锁的必要条件和解决死锁的方法
】必要条件:

  • 资源互斥条件
  • 不可剥夺条件
  • 请求和保持条件
  • 循环等待条件
    死锁解决方法:见脑图
     

问题描述】动态库和静态库的区别
】程序在编译的时候,会把各个源程序编译成目标模块。静态库就是在链接这些模块的时候,把需要的库函数直接链接到目标模块中,形成一个完整的可执行程序。之后程序的运行不需要在包含静态库。动态库的机制是,目标模块的链接不需要把库函数链接进来,而是在程序运行过程中需要库函数的支持时,才链接到库函数。动态库方便库函数的升级,静态库在升级之后,需要重新编译整个程序。
 

问题描述】如何确定计算机是大端存储还是小端存储
小端:高字节存放在高地址,低字节存放在低地址
大端:高字节存放在低地址,低字节存放在高地址

#include <iostream>
using namespace std;

int main()
{
	int a = 1;   //0x00 00 00 01     高<----低 
	char *p = (char* ) &a;
	if(*p == 1){
		cout<<"Little-Endian"<<endl;
	}
	else{
		cout<<"Big-Endiam"<<endl;
	}
}

 

问题描述】介绍一下Linux文件系统中的文件索引节点,硬链接和软链接
】文件系统会为文件维护一个文件目录,文件目录的作用就是可以“按文件名寻找文件”。文件目录中可以直接存放文件的FCB(文件控制块),但是这样的话整个文件目录过大。为了减小文件目录占用的内存空间,把FCB拆分成了文件名其他信息,这些“其他信息”就是文件索引节点(inode)。此时文件目录中存放的就是文件名和指向该文件inode的指针。硬盘在格式化之后,会分成两个区域:数据区用于存放文件数据,索引区用于存放inode节点。
  硬链接和软连接都是Linux中的文件共享方式。硬链接是增加一个文件目录项,该目录的项的指针也指向同一个inode,并且把文件的索引计数加1。所以硬链接不会真正生成任何文件,而只是生成了一个目录项,该目录项也指向文件的索引节点而已。软链接类似于window中的快捷方式,生成一个文件,该文件包含了目标文件的物理路径,需要从目录结构的根节点开始查找,所以软链接查询文件较慢,也会额外消耗磁盘空间。不过软连接利于网络上的文件共享。
 

计算机网络


 
 

智力题


老鼠嗑药

问题描述】你现在有1000瓶药,其中有一瓶有毒,你还有10只老鼠。老鼠在嗑了毒药之后,1个小时会死亡。请问如何使用1个小时判断出哪一瓶药有毒?
】使用二进制方法。对药进行编号1 ~ 1000,在对老鼠进行编号1 ~ 10。比如第123瓶药的10位二进制表示为0001111011,那么就让编号为(1,2,4,5,6,7)号的老鼠嗑这一瓶药。1个小时之后看那些老鼠死了,死了的老鼠对应的二进制为1,活着的老鼠对应的二进制为0。
 

博弈拿石子

问题描述】43个石头,A,B轮流拿,每次可以拿1~3个,A先拿能否保证自己获胜(获胜的条件是做“收尾工作”)?
】当只有1~3个时,A先拿就会赢。当有4个时,A先拿就会输。所以最后只需要剩下4个,然后让B先拿即可。一般规律是,如果有n的石子,A先拿x个,并且保证剩下的石子数量(n-x)是4的倍数,此时A一定可以获胜。对于这道题,A可以先拿3个,此时还剩40个,所以A可以赢。
 

亮着的灯

问题描述】1000盏灯开着,1000个人标号1~1000依次进入,每个人进去按一下自己标号倍数的开关,问最后哪些灯亮着?
】对于一个灯如果被按偶数次开关,则这个灯依然亮着,按奇数次开关,则这个灯灭。一个灯的开关会被什么编号的人按呢?如果一个人的编号是灯编号的因数,则这个人会按该灯的开关。一个数的因数是成对出现的,但是有一种数除外——可以开平方的数,这种数的因数一定有奇数个,比如16的因数有(1,16,2,8,4)。所以灯灭的就是1,4,9,16…这些,其他的都亮着~。

参考链接

  • https://github.com/chankeh/cpp-backend-reference/blob/master/back-end.md
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值