2021腾讯实习一面复盘-小丑竟是我自己

2022-05-10 10:19:07 浏览数 (1)

文章目录

  • 开场
    • 自我介绍
    • 比赛用C ,为何走Java?
    • C/C 和Java有什么区别
  • 手撕算法
    • 连续子数组最大和
    • 合并两个排序链表
  • C/C
    • sizeof
    • union和struct的区别
    • 指针和数组的区别
    • 多态
    • 虚函数
    • static关键字
  • 计网
    • 网络体系结构
    • TCP和UDP的区别
    • TCP为何三次握手
    • TCP滑动窗口
    • TCP和UDP包头大小
    • 网络编程
  • 数据库
    • 描述MySQL索引
  • Linux
    • 进程间通信方式
    • 物理地址和虚拟地址的区别
    • Linux命令
  • Java
    • Java的GC机制
  • 总结

每个岗位和事业群要求肯定不同,题目和答案仅供参考。

开场

自我介绍

主观题

比赛用C ,为何走Java?

主观题

C/C 和Java有什么区别

  • 都是面向对象的语言,都支持继承、封装和多态。
  • Java不提供指针来直接访问内存,程序内存更安全。
  • Java的类是单继承的,C 支持多重继承;虽然Java的类不可以多继承,但是可以实现多个接口。
  • Java有自动内存管理机制,不需要程序员手动释放无用内存。
  • C语言中,字符串或字符数组最后有一个额外的字符’’表示结束,Java中无结束符这一概念。

手撕算法

连续子数组最大和

可参考:数组面试题-大力出奇迹?

合并两个排序链表

可参考:链表面试题(动图详解)-明明做出来了却为什么没有Offer?

C/C

sizeof

代码语言:javascript复制
    double* (*a)[3][6];
    cout << sizeof(a) << endl; // 4 a为指针(64位是8)
    cout << sizeof(*a) << endl; // 72 *a为一个有3*6个指针元素的数组(64位是144)
    cout << sizeof(**a) << endl; // 24 **a为数组一维的6个指针(64位是48)
    cout << sizeof(***a) << endl; // 4 ***a为一维的第一个指针(64位是8)
    cout << sizeof(****a) << endl; // 8 ****a为一个double变量

union和struct的区别

  • Union 整体空间是 占用空间最大的成员(的类型)所占字节数的整倍数。 union的成员共享内存空间,修改某成员值会影响其他成员。
  • Struct 数据对齐原则:内存按结构成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整倍数,如果不够则补齐,以此向后类推。 各成员间互不影响。
代码语言:javascript复制
#include<bits/stdc  .h>
using namespace std;
struct a {
    char x;//sizeof(char)=1
    int y;//sizeof(int)=4
    double z;//sizeof(double)=8
};
union b {
    char x;//sizeof(char)=1
    int y;//sizeof(int)=4
    double z;//sizeof(double)=8
};
int main() {
    struct a t1;
    union b t2;
    cout << sizeof(t1) << endl;//1 4 8 补齐3
    cout << sizeof(t2) << endl;//max(1,4,8)
    t1.x = 'a';
    t2.x = 'a';
    cout << t1.x << " " << t2.x << endl;//a a
    t1.y = 1;
    t2.y = 1;
    cout << t1.x << " " << t2.x << endl;//a 乱码
    cout << t1.y << " " << t2.y << endl;//1 1
    return 0;
}

指针和数组的区别

指针

数组

相当于一个变量,存放的是其它变量在内存中的地址

储存多个相同类型数据的集合

同类型指针可相互赋值

数组只能一个个拷贝元素

存储很灵活,可指向任意类型的数据

存在一块连续的物理空间上,逻辑上的多维数组其实存的是一维

sizeof在32位下是4,64位下是8

sizeof是数组所占存储空间大小,但是传参时会退化为指针,此时sizeof即指针大小

代码语言:javascript复制
#include<bits/stdc  .h>
using namespace std;
void test1(double* c) {
    cout << sizeof(c) << endl;//4
    c[0] = 1.0;
}
void test2(double c[]) {
    cout << sizeof(c) << endl;//4
    c[0] = 2.0;
}
int main() {
    int* a[10];//指针数组:存着10个int*类型指针的数组
    int(*b)[10];//数组指针:即一个指针,指向了一个int[10]的数组
    //换言之 []优先级大于*
    cout << sizeof(a) << endl;//40,10×4(32位指针大小)
    cout << sizeof(b) << endl;//4,即一个指针大小(64位是8)
    cout << sizeof(*a) << endl;//4,数组首指针
    cout << sizeof(*b) << endl;//40,所指数组大小10×4(32位int大小)
    //传参时退化成了指针,是引用传递
    double c[10] = { 0 };
    cout << sizeof(c) << endl;
    cout << c[0] << endl;//0
    test1(c);
    cout << c[0] << endl;//1
    test2(c);
    cout << c[0] << endl;//2
    return 0;
}

多态

多态是同一个行为具有多个不同表现形式或形态的能力。比如重写父类方法、重载同一方法等。

C/C 构成多态条件:

  • 调用函数的对象必须是指针或者引用。
  • 被调用的函数必须是虚函数,且完成了虚函数的重写。
代码语言:javascript复制
#include<bits/stdc  .h>
using namespace std;
class A{ //父类
public:
    virtual void test(){
        cout << "A" << endl;
    }
};
class B : public A{  //子类
public:
    virtual void test(){
        cout << "B" << endl;
    }
};
void fun(A& obj){
    obj.test();
}
int main(){
    A a;
    B b;
    fun(a);//A
    fun(b);//B
    return 0;
}

被调用的函数必须是虚函数,也就是说必须要在两个产生多态的函数前面加virtual关键字。

调用函数的形参对象必须是基类对象,因为派生类能给基类赋值,基类不能给派生类赋值。

调用函数的参数必须是指针或引用,因为派生类改变了虚表,那么这个虚表就属于派生类对象,赋值时只会把基类的成员给过去,虚表指针不会给。

即在调用函数的时候检查,如果满足多态的条件,则触发寻找虚表中虚函数地址。否则会直接用基类对象调用基类函数。

插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/

虚函数

虚函数即在类的成员函数前面加virtual关键字。若派生类中有一个跟基类的完全相同虚函数(函数名、参数、返回值相同),我们就称子类的虚函数重写了基类的虚函数。

协变:重写虚函数的返回值可以不同,但是必须分别是基类指针或引用和派生类指针或引用。(VS会报错,linux的G 不会)

代码语言:javascript复制
class A{ 
public:                                                                  
    virtual A* fun(){
        return new A;                                                    
    }
};
class B : public A{
public:
    virtual B* fun(){
        return new B;
    }
};

纯虚函数:在虚函数的后面写加上’=0’,则这个函数就变成纯虚函数,包含纯虚函数的类叫做抽象类(或接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。

代码语言:javascript复制
class A{
public:
    virtual void test() = 0;
};
class B : public A{
public:
    virtual void Strength(){
        cout << "一键三连" << endl;
    }
};

static关键字

static静态变量作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0,使用时可以改变其值。

  1. 修饰局部变量: 变量在程序初始化时被分配,直到程序退出前才被释放,也就是static是按照程序的生命周期来分配释放变量的,而不是变量自己的生命周期。多次调用也只需一次初始化。
  2. 修饰全局变量: 可在不同的cpp文件中定义同名变量,避免命名冲突(隐藏),保持变量内容的持久。
  3. 类的数据成员: 必须在class的外部初始化。
  4. 类的函数: 不需要类的实例即可调用(注意public和private权限)。属于整个类而非类的对象实例,所以没有this指针。静态成员之间可以相互访问。
  5. 单例模式中使用,保证一个类在内存中仅有一个实例,并提供一个访问它的全局访问点。

计网

网络体系结构

TCP和UDP的区别

  • TCP连接可靠安全有序一对一较慢
  • UDP无连接不可靠不安全无序一对多较快

TCP为何三次握手

A=>B:A不知道自己发送是否成功,B收到后知道A发送正常、B接收正常。

B=>A:B不知道自己发送是否成功,A收到后知道A收发正常,B收发正常。

A=>B:B收到后知道B收发正常、A收发正常。

TCP滑动窗口

发送窗口不断向前滑动,是一种连续的AQR协议。允许发送方已发送但还没有收到确认的分组序号的范围,窗口大小是发送方已发送未确认的最大分组数。避免单窗口的一直等待一个ack而延迟阻塞。

  1. 接收方按序接收分组。当收到失序的分组,它会丢弃,并对按序的分组进行确认。
  2. 接收方采用累计确认的方式。在收到n个分组之后,对按序到达的最后一个分组进行确认。
  3. 发送方采用超时重传机制来重传差错或丢失的分组。一旦分组出错,在该分组之后发送的所有分组都需要重传,退回去重传已发送的N个分组,故称为回退N帧协议(GBN)。

TCP和UDP包头大小

TCP:20

UDP:8

网络编程

可参考:Python网络编程-一文厘清socket、TCP和UDP那点事

数据库

描述MySQL索引

可参考:不懂就问,MySQL索引是啥?

Linux

进程间通信方式

  • 管道 父子进程间使用,无格式字节流,慢
  • 信号量 计数器、锁机制、控制多个进程对共享资源的访问,承载信号量小,同步问题
  • 消息队列 消息的链接表,注意读写队列中消息的权限,容量限制,注意上次是否读完
  • 共享内存 银蛇一块可被其他进程所访问的内存,快,同步问题
  • 信号 通知接收进程某个事件已经发送
  • 套接字 可用于不同机器间进程通信

物理地址和虚拟地址的区别

  • 物理地址:CPU地址总线传来的地址,由硬件电路控制其具体含义。一部分给物理RAM(内存)用,一部分给总线用。
  • 虚拟地址:现代操作系统普遍采用虚拟内存管理机制, 需要MMU(Memory Management Unit)的支持,也就是将虚拟地址映射成物理地址。

Linux命令

可参考:Linux-基础实用指令(不会还有人不知道吧)

Java

Java的GC机制

可参考:还不会JVM,是准备家里蹲吗?

总结

约75分钟,中间几次声音听不清(不知道是网络还是火狐的锅,推荐Chrome)。几度怀疑走错C/C 片场,Java的问题只有一个GC,准备的好多东西都没有问到,像hashmap、设计模式、redis、JVM还有项目啥的。好吧,还是我太菜了。渡人易渡己难,如果能帮到正努力进鹅厂的你也好,冲!

0 人点赞