面试记录

隔了几天,重新回忆回忆我到底面了啥,我写了啥,我说了啥。

面试官:看了项目都是玩具代码

我: 嗯嗯嗯(小鸡啄米)

面试官:你毕业6月份,两个月你怎么不找工作呢?

我:在学您说的玩具代码。

面试官:没什么比在实际项目中学习最好的,年轻人再拷打拷打吧

手写代码

前置++与后置++的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyClass {
public:
int value;

MyClass(int val) : value(val) {}

// 重载前置递增运算符
MyClass& operator++() {
// 实现递增操作
++value;
return *this; // 返回递增后的对象的引用
}
//重载后置递增运算符
MyClass operator++(int) {
MyClass temp(value);
// 实现递增操作
value++;
return temp; // 返回递增前的对象的副本
}
};

memcpy的简单实现

嘛,就按照字节一个一个copy呗。

1
2
3
4
5
6
7
8
9
10
11
12
13
void* memcpy(void* dest, const void* src, size_t count) {
//这里应该可以用static_cast 装一下,美名其曰更安全。
char* dest_char = (char*)dest;
const char* src_char = (const char*)src;
// 检查源和目标内存是否重叠
if (dest_char == src_char) {
return;
}
// 从源内存复制到目标内存按一个字节
for (size_t i = 0; i < count; ++i) {
dest_char[i] = src_char[i];
}
}

大小端转换

将一个unsigned long val 字节序转换 假设 0值为0x123456
我这题写了一个方式,并说出了大小端区别,主机和网络字节序的关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//我写的union方法
union converter{
unsigned long value;
unsigned char bytes[sizeof(unsigned long)];
};

int main(){
converter c;
c.value = 0x123456;
}

//第二种方法就是中规中矩 的 按照大小端区别倒换一下

unsigned long value = 0x123456;
char bytes[sizeof(unsigned long)];

for (size_t i = 0; i < sizeof(unsigned long); ++i) {
bytes[i] = (char*)((value >> (i * 8)) & 0xFF);
}

ps :这里补个检测大小端的代码,利用GCC编译器提供的宏来处理

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main() {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
printf("Little-Endian\n");
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
printf("Big-Endian\n");
#else
printf("Unknown Endian\n");
#endif
return 0;
}

string类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//写了一个
class String {
private:
char* str; // 字符串存储
size_t length; // 字符串长度

public:
// 构造函数
String(const char* s = "") {
length = strlen(s);//这里问了怎么实现的strlen ,我的回答是以\0判断字符串结尾。
str = new char[length + 1];
strcpy(str, s);
}

// 拷贝构造函数
String(const String& other) {
length = other.length;
str = new char[length + 1];
strcpy(str, other.str);
}

// 析构函数
~String() {
delete[] str;
}

// 获取字符串长度
size_t size() const {
return length;
}

// 获取C字符串
const char* c_str() const {
return str;
}

// 重载赋值运算符
String& operator=(const String& other) {
if (this != &other) {
delete[] str;
length = other.length;
str = new char[length + 1];
strcpy(str, other.str);
}
return *this;
}
};

项目内容:

UDP 穿透 NAT环境

  • 你是利用UDP穿透实现的跨局域网的控制吗?

嗯,是的,但是我实际上没有完成这项功能,因为,NAT 的环境的复杂性,我没有再继续进行测试,做了个初步的调查,UDP穿透是可行的跨局域网的手段。嗯, NAT 是 Net Address translation 吧,就是网络地址转换,还有一个NAPT,网络地址端口转换,这几个协议,忘记是哪个层的了,应该是物理层,在路由器这块的,是解决IPv4资源不够用的方法,也有隔离保护的作用。NAT有全开放的,有锥形限制的,只限制IP的,和只限制端口的,两者都限制的。都需要一个公网服务器帮助进行穿透。

屏幕控制

  • 关于这个屏幕控制,具体实现是怎样的?

主要完成了就是,被控端的屏幕的显示,鼠标控制。屏幕画面来源于TCP 包传输,该包包含了被控端的屏幕图片帧数据。这个帧数据是通过,嗯WINDOWS的GDI 的系统函数,屏幕截图的函数拿到的,并编码为png在进行打包传递,这里我测试了两种图片编码格式 jpg 和png ,png占优。控制端,接受到并进行解包显示,使用到定时器的事件,不是,是MFC 的 消息,这个客户端是在WINDOWS下基于MFC实现的,嗯,这个功能是开启个线程下完成的,保证显示屏幕功能不阻塞接受包解包的功能。

鼠标控制就是利用WINDOWS 的 消息事件来实现的。需要考虑到被控屏幕和鼠标操作实际消息触发的位置,进行一个偏移转换。

MFC 与 MVC

  • 你确定你这个远程控制的项目是MFC 不是MVC 你没有搞错吗?

我确定就是MFC做的远程控制。

MVC是 model view control 就是模块层 视图层 控制层,远控的客户端也是基于MVC架构实现的。

总结

还有一些笔试题,也不难,考察的都是C++基础。职业规划都讲的挺清楚,入职后谁来带,公司的业务,公司的状态,公司的后续计划,面试官都主动的告知我,让我一度有种已经拿到offer的感觉,告诉我后续等人事 HR详说,结果过了两天,了无音信。。