Linux
腾讯⾳乐 C++ ⾯试
腾讯⾳乐的校招薪资 。
腾讯今年的校招薪资是有新变化 的,也就是将 16 薪资改为了 15 薪,最后⼀薪直接加到 每⽉的
⽉薪⾥了,每个⽉到⼿的钱相⽐以前多了,但是整体收⼊是没有变化 的,⽐如原本 20k * 16 ,变成
了 21.6 x 15 。
腾讯⾳乐薪资⼀样也是变化 了,16 薪变为了 15 薪。
可惜⽬前, 25 届腾讯⾳乐开发岗的薪资数据不多,⽬前只发现了下 ⾯这⼀个:
25.5k x 15 + 2k x 12 (房补) + 6w (股票分 2 年发) + 3w (签字费)= 46w 总包
这⾥也贴⼀下往届腾讯⾳乐开发岗的校招薪资情况,那时候是 16 薪,可以作 为⼀个参考:
24k x 16 + 3k x 12 (房补)+5w (股票分 2 年发)+ 5w (签字费)= 49.5w 总包
23k x 16 + 3k x 12 (房补)+ 3w (股票分 2 年发) + 3w (签字费)= 44.9w 总包
20.5k x 16 + 3k x 12 (房补) + 3w (签字费)= 39.4w
19.5k x 16 + 3k x 12 (房补) + 2w (签字费) = 36.8w腾讯⾳乐的⾯试跟腾讯没有什么 太⼤的区别,都是⼤⼚⾯试难度,之前也有训练营 同学拿到了
腾讯⾳乐的 offer ,⾯完泡了很久,才开出来了,这种⼤概率是前⾯有⼈鸽了,等等 党的胜利了。
不过,也看到有同学⾯腾讯⾳乐的时候,感觉题⽬不是很难,就是基础问题,⽽且也 感觉⾯试官
在对着题库念,回答完之后也没有反应,说完⼀个问题就直接开始下⼀个问题,这场⾯试有点机
械感,不出所料,最后还是挂了,让同学觉得是 kpi ⾯。
然后,也有同学看了这个⾯经,说跟他当时⾯的时候⼀模⼀样,算法题都⼀样,结果也是挂了。
那这次我们就来看看 同学所说的腾讯⾳乐的 “KPI” 后端开发的⼀⾯,同学的技术栈是 C++ ,所以
主要是问了C++ 、Linux 、操作系统、数据库、算法这些知识。

最常⽤的语⾔是什么 ?
我常⽤的是c++
C++ malloc 和new 的区别
语法不同:malloc/free 是⼀个C语⾔的函数,⽽new/delete 是C++ 的运算符 。
分配内存的⽅式不同:malloc 只分配内存,⽽new 会分配内存并且调⽤对象的构造函数来初始化
对象。
返回值不同:malloc 返回⼀个 void 指针,需要⾃⼰强制类型转换,⽽new 返回⼀个指向对象类
型的指针。
malloc 需要传⼊需要分配的⼤⼩,⽽ new 编译器会⾃动计算所构造对象的⼤⼩
C++ 多态特性是什么 ?
多态是⾯向对象编程(OOP )的重要特性之⼀,它允许不同的对象对同⼀消息(函数调⽤)做出
不同的响应。在 C++ 中,多态主要通过虚函数来实现。
多态有静态多态和动态多态两种:
** 静态多态(编译时多态):** 主要通过函数重载来实现。函数重载是指在同⼀个作⽤域内,可
以有多个同名 函数,但是它们的参数列表(参数个数、参数类型或者参数顺序)不同。例如:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}当调⽤ add 函数时,编译器会根据传⼊的参数类型和个数在编译时期就确定调⽤哪个版本的 add
函数。
** 动态多态(运⾏时多态):** 基于虚函数和继承来实现。它允许在运⾏时根据对象的实际类型
来调⽤相应的函数。当⼀个类包含虚函数时,编译器会为这个类创建⼀个虚函数表。虚函数表
是⼀个函数指针数组,其中存储了这个类的虚函数的地址 。每个包含虚函数的类的对象中都会
包含⼀个虚函数指针,这个指针指向该类的虚函数表。当通过基类指针或引⽤调⽤虚函数时,
程序会根据虚函数指针找到对应的虚函数表,然后在虚函数表中查找要调⽤的虚函数的实际地址,从⽽实现根据对象的实际类型来调⽤函数。
Linux
⽤过哪些Linux 命令?
** ⽂件和⽬录操作命令:**ls 、cd 、mkdir 、pwd 、cp 、mv 、rm
** ⽂件内容查看和编辑命令:cat 、**tail 、less 、more 、head 、vi
** 系统信息查看命令:**ps 、top 、free 、df 、du
** ⽤⼾和权限管理命令:**chmod
** ⽹络相关命令:**tcpudmp 、ifconfig 、ping 、netstat 、wget
如何结束进程名为aaa 的进程?
⾸先,需要找到进程名为 “aaa” 的进程 ID (PID )。可以使 ⽤ pgrep 命令来查 找, pgrep 命令
⽤于查找当前运⾏进程的 PID ,它会根据进程名称进⾏匹配。命令格式为 pgrep [ 选项 ] 进程
名 。例如, pgrep aaa 会返回名为 “aaa” 的所有进程的 PID 。
然后,使⽤ kill 命令来结束进程。 kill 命令⽤于向指定的进程发送信号,默认发送的是
SIGTERM 信号,该信号会请求进程正常终⽌。⼀般情况下,进程收到这个信号后 会进⾏⼀些清
理⼯作然后终⽌。命令格式为 kill [ 选项 ] PID 。例如,如果通过 pgrep aaa 得到的 PID 是1234 ,那么可以使 ⽤ kill 1234 来结束这个进程。
如果进程对 SIGTERM 信号⽆响应,还可以使 ⽤ kill -9 PID (其中 -9 表⽰发送 SIGKILL 信
号)来强制结束进程。不过这 种⽅式可能会导致进程没有机 会进⾏清理操作,有可能丢失数
据,应该谨慎使⽤。例如, kill -9 $(pgrep aaa) 会强制结束所有名为 “aaa” 的进程。多进程和多线程的区别?
资源分配⽅⾯的区别:
多进程:每个进程都有独⽴的地址 空间,这意味着它们在内存中有⾃⼰独⽴的区域来存储代
码、数据和堆栈等。例如,⼀个进程的全局变量在另⼀个进程中是不可⻅的,进程之间的数据
交换相对复杂,需要通过进 程间通信(IPC )机制,如管道、消息队列、共享内存等来实现。
多线程:线程共享进程的地址 空间,因此线程之间共享全局变量和堆内存等资源。这使得线程
之间的数据共享相对简单,⽐如在⼀个多线程的服务器程序中,多个线程可以直接访问和修改
共享的数据结构,如连接队列等。线程的创建和销毁相对进程来说开销较⼩,因为它们共享了
进程的⼤部分资源,不需要重新分配像进程那样完整的资源集合。不过,由于线程共享资源,
可能会导致资源竞争问题,需要通过同步机制(如互斥锁、信号量等)来解决。
调度和执⾏⽅⾯的区别:
** 多进程:** 进程间的切换开销相对较⼤,因为涉及到整个地址 空间的切换、保存和恢复进程的
上下 ⽂(包括寄存 器的值、程序计数器等)。例如,从⼀个运⾏的⽂本编辑器进程切换到浏览器
进程时,需要保存⽂本编辑器进程的完整状态,然后加载浏览器进程的状态。。
多线程:线程之间的切换相对进程来说开销较⼩,因为它们共享地址 空间,只需要切换线程的
私有数据(如栈指针、程序计数器等)和保存恢复⼀些寄存 器的值。
稳定性和可 靠性⽅⾯:
多进程:⼀个进程的崩溃(例如出现段错误等)通常不会影响其他进程的正常运⾏,因为每个
进程都有独⽴的地址 空间和资源。这使得系统的稳定性相对较⾼,例如,在⼀个服务器系统
中,即使⼀个服务进程(如邮件服务进程)崩溃,其他服务进程(如 Web 服务进程)仍然可以
继续⼯作。
多线程:由于线程共享进程的资源,⼀个线程出现问题(如访问⾮法地址 、死锁等)可能会导
致整个进程崩溃。例如,在⼀个多线程的数据库服务器程序中,如果⼀个线程因为错误的内存
访问⽽崩溃,可能会导致整个服务器进程退出,影响系统的可靠性。
什么 情况下使⽤多进程更好?
安全性和稳定性:当程序的不同部分对稳定性和安全性要求很⾼,并且彼此之间的错误可能会
相互影响时,多进程是很好的选择。例如,在⼀个服务器系统中,有 Web 服务进程和数据库服
务进程。如果 Web 服务进程因为遭受恶意攻击或者出现代码错误⽽崩溃,由于数据库服务进程
是独⽴的,其数据和运⾏不会受到直接影响,从⽽保证了数据的安全性和系统的部分功 能依然
可⽤。
不同语⾔的兼容性:如果需要在⼀个应⽤程序中集成多种编程语⾔编写的模块,多进程可以提
供简单的解决⽅案。例如,有⼀个系统,⼀部分是⽤ Python 编写的数据分析模块,另⼀部分是
⽤ C++ 编写的⾼性能计算模块。由于不 同语⾔的运⾏时环境、内存管理等可能存在差异,使⽤
多进程可以让每个模块在 ⾃⼰独⽴的进程中运⾏,避免语⾔之间的相互⼲扰。
数据库
⽤过什么 数据库?
主要⽤过 mysql
mysql 的引擎是什么 ?
InnoDB :InnoDB 是MySQL 的默认存储引擎,具有ACID 事务⽀持、⾏级锁、外键约束等特性。
它适⽤于⾼并发的读写操作,⽀持较好的数据完整性和并发控制。
MyISAM :MyISAM 是MySQL 的另⼀种常⻅的存储引擎,具有较低的存储空间和内存消耗,适⽤
于⼤量读操作的场景。然⽽,MyISAM 不⽀持事务、⾏级锁和外键约束,因此在并发写⼊和数据
完整性⽅⾯有⼀定的限制。
Memory :Memory 引擎将数据存储在内存中,适⽤于对性能要求较⾼的读操作,但是在服务器
重启或崩溃时数据会丢失。它不⽀持事务、⾏级锁和外键约束。
innodb 有什么 特性?
InnoDB 引擎在事务⽀持、并发性能、崩溃恢复等⽅⾯具有优势,因此被MySQL 选择为默认的存储
引擎。
事务⽀持:InnoDB 引擎提供了对事务的⽀持,可以进⾏ACID (原⼦性、⼀致性、隔离性、持久
性)属性的操作。Myisam 存储引擎是不⽀持事务的。
并发性能:InnoDB 引擎采⽤了⾏级锁定的机制,可以提供更好的并发性能,Myisam 存储引擎只
⽀持表锁,锁的粒度⽐较⼤。
崩溃恢复:InnoDB 引引 擎通过 redolog ⽇志实现了崩溃恢复,可以在数据库发⽣异常情况(如
断电)时,通过⽇志⽂件进⾏恢复,保证数据的持久性和⼀致性。Myisam 是不⽀持崩溃恢复
的。
innodb 和其他引擎相⽐有什么不 同
mysql 的innodb 与MyISAM 的区别如下:
事务:InnoDB ⽀持事务,MyISAM 不⽀持事务,这是 MySQL 将默认存储引擎从 MyISAM 变成
InnoDB 的重要原因之⼀。
索引结构:InnoDB 是聚簇索引,MyISAM 是⾮聚簇索引。聚簇索引的⽂件存放在主键索引的叶
⼦节点上,因此 InnoDB 必须要有主键,通过主键索引效率很⾼。但是辅助索引需要两次查
询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过⼤,因为主 键太⼤,其
他索引也都会很⼤。⽽ MyISAM 是⾮聚簇索引,数据⽂件是分离的,索引保存的是数据⽂件的
指针。主键索引和辅助索引是独⽴的。
锁粒度:InnoDB 最⼩的锁粒度是⾏锁,MyISAM 最⼩的锁粒度是表锁。⼀个更新语句会锁住整
张表,导致其他查询和更新都会被阻塞,因此并发访问受限。
count 的效率:InnoDB 不保存表的具体⾏数,执⾏ select count(*) from table 时需要全表扫描。⽽MyISAM ⽤⼀个变量保存了整个表的⾏数,执⾏上述语句时只需要读出该变量即可,速
度很快 。
mysql 的数据结构是什么
MySQL InnoDB 引擎是⽤了B+ 树作为了 索引的数据结构。
B+Tree 是⼀种多叉树,叶⼦节点才存放数 据,⾮叶⼦节点只存放索引,⽽且每个节点⾥的数据是
按主键顺序存放的。每⼀层⽗节点的索引值都会出现在下层⼦节点的索引值中,因此在叶⼦节点
中,包括了所有的索引值信息,并且每⼀个叶⼦节点都有两个 指针,分别 指向下⼀个叶⼦节点和
上⼀个叶⼦节点,形成⼀个双向链表。
主键索引的 B+Tree 如图所⽰:

⽐如,我们执⾏了下 ⾯这条查 询语句:
这条语句使⽤了主 键索引查询 id 号为 5 的商品。查询过程是这样的,B+Tree 会⾃顶向下逐层进⾏
查找:
将 5 与根节点的索引数据 (1 ,10 ,20) ⽐较,5 在 1 和 10 之间,所以根据 B+Tree 的搜索逻
辑,找到第⼆层的索引数据 (1 ,4,7) ;
在第⼆层的索引数据 (1 ,4,7) 中进⾏查找,因为 5 在 4 和 7 之间,所以找到第三层的索引数
据(4,5,6);在叶⼦节点的索引数据(4,5,6)中进⾏查找,然后我们找到了索引值为 5 的⾏数据。
select * from product where id = 5;数据库的索引和数据都是存储在硬盘的 ,我们可以把读取⼀个节点当作⼀次磁盘 I/O 操作。那么上⾯的整个查询过程⼀共经历了 3 个节点,也就是进⾏了 3 次 I/O 操作。
B+Tree 存储千万级的数据只需要 3-4 层⾼度就可以满⾜,这意味着从千万级的表查询⽬标数据最
多需要 3-4 次磁盘 I/O ,所以B+Tree 相⽐于 B 树和⼆叉树来说,最⼤的优势在于查询效率很⾼,
因为即使在数据量很⼤的情况,查询⼀个数据的磁盘 I/O 依然维持在 3-4 次。
算法
字符串相加
解题思路:将两个 字符串表⽰的数字看作是按位排列的数字,从低位 (字符串末尾)开始逐位相
加,同时考虑进位情况,就如同我们在纸上进⾏竖式加法运算⼀样。
代码实现:
#include <iostream >
#include <string >
using namespace std ;
string addStrings (string num1 , string num2 ) {
string result ;
int i = num1 .size () - 1, j = num2 .size () - 1;
int carry = 0; // 进位
while (i >= 0 || j >= 0 || carry > 0) {
int n1 = i >= 0? num1 [i--] - '0' : 0;
int n2 = j >= 0? num2 [j--] - '0' : 0;
int sum = n1 + n2 + carry ;
carry = sum / 10 ;
result .push_back (sum % 10 + '0' );
}
return string (result .rbegin (), result .rend ());
}
int main () {
string num1 = "123" ;
string num2 = "456" ;
string sum = addStrings (num1 , num2 );
cout << sum << endl ;
return 0;
}给⼀个整数 ,求其⼆进制1的个数,负数取补码
解题思路:通过循环对整数 的每⼀位进⾏判断,利⽤位运算中的与运算( & )来检查当前位是否为 1。具体来说,将整数 与 1 进⾏与运算,如果结果为 1,则表⽰当前最低位 是 1,然后将整数 右
移⼀位,继续检查下⼀位,直到整数 变为 0 为⽌。
代码实现:
#include <iostream >
using namespace std ;
int countOnes (int num ) {
int count = 0;
while (num != 0) {
if (num & 1) {
count ++ ;
}
num >>= 1;
}
return count ;
}
int main () {
int num = -5;
int result = countOnes (num );
cout << "二进制表示中 1的个数为 : " << result << endl ;
return 0;
}