由于前一天孩子打了疫苗然后晚上发烧了,睡觉的时候差不多凌晨四五点了,一晚上睡了两三个小时,在这之前呢,上午十一点面了一家,下午两点半又面试了一家,下午四点半又面试了一家,面试这家的时候是第四家,脑袋属于迷糊状态。
总共2次技术面试,第一面和第二面恰巧是同一个面试官,问了很多的问题,也聊了很多,虽然自己感觉面试的一塌糊涂,最后面试官给过了,面试官人很不错,2面直接给了offer,很感谢这位面试官。可是因为其他原因,我选择了拒绝,这里不能说是哪家公司,希望这位面试官步步高升。
把面试回答不好的地方做以下总结。及时的查漏补缺。
另外一点总结就是:面试的时候,别管前一个问题回答的如何,下一个题目一定要认真仔细的回答清楚,面试是一个很好的学习机会。
问题1:找出下面代码的错误,并指明错误原因,并说明printf能否打印
#include <iostream>
#include <cstdio>
int main(void)
{
char *p = new char[100];
p += 2;
delete []p;
printf("it is demo\n");
}
运行结果:
gdb调试:
结论:
printf不能打印。p是一个无效的指针,会出现核心转储
原因(题目考察的核心点):
只有 malloc返回的地址才能free()
new和delete本质也是调用的malloc和free。
改正:
#include <iostream>
#include <cstdio>
int main(void)
{
char *p = new char[100];
//p += 2;
delete []p;
printf("it is demo\n");
}
问题2:下面代码的printf能否打印?错误在哪?
#include <iostream>
#include <cstdio>
struct MyClass{
int a;
};
int main(void)
{
char *p = new char[100];
delete p;
printf("it is test demo1\n");
MyClass* q = new MyClass [100];
delete q;
printf("It is a test demo2!\n");
}
其实这个问题很简单:
(1)printf可以正常打印
(2)会出现内存泄漏
问题3:下一段代码能否正常打印?
#include <iostream>
#include <cstdio>
int main(void)
{
char* ptr = "0123456789";
while(*ptr)
{
printf("%c\n", *ptr++);
}
return 0;
}
结论:这段代码可以正常打印如下结果:
gdb调试程序也是正常退出
问题四:下段代码输出什么?
#include <iostream>
#include <cstdio>
int main(void)
{
char* ptr = "0123456789";
while(*ptr++)
{
printf("%c\n", *ptr);
}
return 0;
}
结论:
为什么会多一个换行呢?
原因:最后的'\0'也会被计算到里面,所以最后多了一个换行
考察的主要问题是:*和++运算符的优先级,*的优先级高于++的优先级,那这个问题就很好理解了
问题5:下段代码输出什么?
#include <iostream>
#include <cstdio>
using namespace std;
int main(void)
{
unsigned char aaa[100] = {0};
int bbb[200] = {0};
cout << sizeof(aaa) << "," << sizeof(bbb) << endl;
int var,count,incr;
(count=0, incr=10, count++) ? var = (incr-=2, count+2) : var = (incr--, count++);
cout << incr << "," << var << endl;
return 0;
}
分析:
(1)前半段呢,都没问题
(2)后半段呢,是个三目描述符,首先我第一次见到这种三目,当时有点木,真的,后来仔细分析了如下:
1)(count=0, incr=10, count++):主要是判断这个表达式是不是为真。这两个数字在三目判断的时候分别为:incr=10, count=0,所以逻辑判断为假。
2)逻辑判断为假,执行 var = (incr--, count++), 这个的意思是:执行括号里面的操作,
执行结果为:
问题6:下段代码输出什么?
#include <iostream>
#include <cstdio>
using namespace std;
int func(int x) {
int count = 0;
while(x)
{
count++;
x = x&(x-1);
}
return count;
}
int main(void)
{
cout << " func(255) = " << func(255) << endl ;
return 0;
}
唉,其实这个是一个按位运算
Starting program: /home/mrlee/test/c++/a.out
Breakpoint 1, main () at main.cpp:20
20 cout << " func(255) = " << func(255) << endl ;
(gdb) s
func (x=255) at main.cpp:9
9 int count = 0;
(gdb) n
10 while(x)
(gdb) wathc x
Undefined command: "wathc". Try "help".
(gdb) watch x
Hardware watchpoint 2: x
(gdb) watch count
Hardware watchpoint 3: count
(gdb) n
12 count++;
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 0
New value = 1
func (x=255) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 255
New value = 254
func (x=254) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 1
New value = 2
func (x=254) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 254
New value = 252
func (x=252) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 2
New value = 3
func (x=252) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 252
New value = 248
func (x=248) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 3
New value = 4
func (x=248) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 248
New value = 240
func (x=240) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 4
New value = 5
func (x=240) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 240
New value = 224
func (x=224) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 5
New value = 6
func (x=224) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 224
New value = 192
func (x=192) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 6
New value = 7
func (x=192) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 192
New value = 128
func (x=128) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Hardware watchpoint 3: count
Old value = 7
New value = 8
func (x=128) at main.cpp:13
13 x = x&(x-1);
(gdb) c
Continuing.
Hardware watchpoint 2: x
Old value = 128
New value = 0
func (x=0) at main.cpp:10
10 while(x)
(gdb) c
Continuing.
Watchpoint 2 deleted because the program has left the block in
which its expression is valid.
Watchpoint 3 deleted because the program has left the block in
which its expression is valid.
0x00000000004008d1 in main () at main.cpp:20
20 cout << " func(255) = " << func(255) << endl ;
(gdb) p func(255)
$1 = 8
(gdb)
返回8
个人觉得最好是手画一画,其实这个就是255最大是8个位全为1,没减1,向前一位。
1111 1111
& count=1
1111 1110
x = 254
1111 1110
& count=2
1111 1101
x = 252
1111 1100
& count=3
1111 1011
x = 248
1111 1000
& count =4
1111 0111
x=240
1111 0000
& count=5
1110 1111
x=224
1110 0000
& count=6
1101 1111
x =192
1100 0000
& count=7
1011 1111
x=128
1000 0000
& count=8
1111 1111
x=0;
问题7:下面输出是啥?
map<int, string> mapStudent;
map<int, string>::iterator iter;
mapStudent.insert(map<int, string>::value_type (1, "student_one"));
mapStudent.insert(map<int, string>::value_type (1, "student_two"));
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
mapStudent[1] = "student_three";
mapStudent[1] = "student_four";
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
for(int i = 1; i < 3; i++)
cout<< mapStudent[i] <<endl;
cout<< mapStudent.size() << endl;
问题8:下面代码是否有问题?输出是?
vector<int>demo{0,1,2,3,4,5,6,7,8,9,10 };
vector<int>::iterator it = demo.begin();
for(it = demo.begin(); it != demo.end(); it++)
{
if(!(*it%2))
demo.erase(it);
}
for(it = demo.begin(); it != demo.end(); it++)
cout << *it << endl;
问题9:下面代码有没有问题?
struct ComplexNumber
{
int Real_Quantity;
int Imaginary_Quantity;
};
multiset<ComplexNumber > dataset;
正在学习STL相关的,后期一定补齐这几个问题(7, 8, 9)
问题10.网络序列化问题?
网络传输中,牵扯到数据序列化,反序列化。其实,通信双方约定好序列化方式(大端/小端)即可。例如发送方按照大端序列化,接收端在接收到数据后,接收端判断自己的大小端模式,如果自己的CPU是大端模式,则不需要做大小端转换,直接进行数据解析即可。如果是小端,则解析完后还需要将数据转为小端模式。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
说的再直接点,序列化的目的就是为了跨进程传递格式化数据
大端:数据高字节内容保存在内存的低地址中,数据的低字节内容保存在内存的高地址中。
小端:数据高字节内容保存在内存的高地址中,数据的低字节内容保存在内存的低地址中。
比如json或者xml等,其实在发送的时候需要序列化,接收的时候需要反序列化。
问题11.http中的post和put的区别?
1.put和post的区别
PUT和POST都有更改指定URI的语义.但PUT被定义为idempotent的方法,POST则不是.idempotent的方法:如果一个方法重复执行多次,产生的效果是一样的,那就是idempotent的。也就是说:
PUT请求:如果两个请求相同,后一个请求会把第一个请求覆盖掉。(所以PUT用来改资源)
Post请求:后一个请求不会把第一个请求覆盖掉。(所以Post用来增资源)
2、get和post
- GET参数通过URL传递,POST放在Request body中。
- GET请求会被浏览器主动cache,而POST不会,除非手动设置。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
- Get 请求中有非 ASCII 字符,会在请求之前进行转码,POST不用,因为POST在Request body中,通过 MIME,也就可以传输非 ASCII 字符。
- 一般我们在浏览器输入一个网址访问网站都是GET请求
- HTTP的底层是TCP/IP。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。但是请求的数据量太大对浏览器和服务器都是很大负担。所以业界有了不成文规定,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。
- GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
- 在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。但并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
问题12:Http如何做长链接?
首先http中,没有长连接这样的类型。
http要实现长连接,是建立在tcp协议的基础上的。
一个http连接,等到不再需要该连接的时候,主动调用该连接的close()方法,才会关闭该连接。
HTTP短连接:
客户端和服务端进行一次HTTP请求/响应之后,就关闭连接。下一次的HTTP请求/响应操作需要重新建立。
在首部字段中设置Connection:close,则在一次请求/响应之后,就会关闭连接。
HTTP长连接:
客户端和服务端建立一次连接之后,可以在这条连接上进行多次请求/响应操作。
持久连接可以设置过期时间,也可以不设置。
在首部字段中设置Connection:keep-alive 和 Keep-Alive:timeout = 60,
表明连接建立之后,空闲时间超过60秒,连接失效。
如果在空闲第58秒使用此连接,则仍然有效,
并且使用完之后,重新计数空闲时间,空闲60秒无再使用,连接失效。
设置HTTP长连接,无过期时间,在首部字段中只设置Connection:keep-alive,表明连接永久有效。
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。
实现长连接要客户端和服务端都支持长连接。
HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。
13.个人觉得HTTP链接时间设置思想
(1)进程中有两个线程,线程1专门做数据的收和发,线程2做定时任务(红黑树)。
(2)线程1:在接收到数据后,解析出header中的Connecttion对应的value,如果,value=keep-alive,表示长链接,不做任何处理。如果value=timeout=?此类的值,首先判断红黑树是否存在对应的链接fd,如果不存在增加节点,如果存在,修改。把对应的?解析出来,然后使用当前时间+超时时间为key值加入红黑树。每次在收到数据的时候,把对应时间重置为当前时间+延迟时间。
(3)线程2,做TCP client+定时红黑树。 在对应的时间到时见后,把对应的fd发送给线程1,由线程1决定此fd是做close动作还是重置动作(可以也是用http协议,做put请求,防止重复close)。
(4)线程1,如果收到线程2的put请求后,判断是重置还是close,如果是close,则首先把红黑树的此节点删除,然后,调用close fd。
线程1主要负责:
- 对红黑树进行插入、删除、修改动作
- 对客户端请求(http)协议的解析和响应、以及断开等操作
线程2主要负责:
- 对红黑树进行查询、到时发送给线程1。
总结性能件:
- 线程1,放到主进程里面,主要做检测(epoll)
- 线程2,单独启动的特殊线程。
- 在加一个线程池,专门处理事件
问题14:如何通过进程PID获取进程的工作目录等信息?
其实这个题目的核心点,进程启动后,此进程的详细信息在哪里查看?
进程启动后,会在/proc/目录下,生成一个与此PID对应的文件夹,这个文件夹里面包含了此进程的的详细信息
1.获取进程的PID
ps -aux|grep "进程名称"
2.获取进程的工作目录
mrlee@mrlee-virtual-machine:~/test/c++$ ll /proc/7489
总用量 0
dr-xr-xr-x 9 mrlee mrlee 0 7月 21 16:39 ./
dr-xr-xr-x 262 root root 0 7月 21 11:58 ../
dr-xr-xr-x 2 mrlee mrlee 0 7月 21 16:41 attr/
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 autogroup
-r-------- 1 mrlee mrlee 0 7月 21 16:41 auxv
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 cgroup
--w------- 1 mrlee mrlee 0 7月 21 16:41 clear_refs
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:39 cmdline
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 comm
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 coredump_filter
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 cpuset
lrwxrwxrwx 1 mrlee mrlee 0 7月 21 16:41 cwd -> /home/mrlee/test/c++/
-r-------- 1 mrlee mrlee 0 7月 21 16:41 environ
lrwxrwxrwx 1 mrlee mrlee 0 7月 21 16:41 exe -> /home/mrlee/test/c++/a.out*
dr-x------ 2 mrlee mrlee 0 7月 21 16:39 fd/
dr-x------ 2 mrlee mrlee 0 7月 21 16:41 fdinfo/
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 gid_map
-r-------- 1 mrlee mrlee 0 7月 21 16:40 io
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 limits
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 loginuid
dr-x------ 2 mrlee mrlee 0 7月 21 16:41 map_files/
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 maps
-rw------- 1 mrlee mrlee 0 7月 21 16:41 mem
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 mountinfo
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 mounts
-r-------- 1 mrlee mrlee 0 7月 21 16:41 mountstats
dr-xr-xr-x 5 mrlee mrlee 0 7月 21 16:41 net/
dr-x--x--x 2 mrlee mrlee 0 7月 21 16:41 ns/
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 numa_maps
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 oom_adj
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 oom_score
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 oom_score_adj
-r-------- 1 mrlee mrlee 0 7月 21 16:41 pagemap
-r-------- 1 mrlee mrlee 0 7月 21 16:41 patch_state
-r-------- 1 mrlee mrlee 0 7月 21 16:41 personality
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 projid_map
lrwxrwxrwx 1 mrlee mrlee 0 7月 21 16:41 root -> //
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 sched
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 schedstat
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 sessionid
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 setgroups
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 smaps
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 smaps_rollup
-r-------- 1 mrlee mrlee 0 7月 21 16:41 stack
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:39 stat
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 statm
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:39 status
-r-------- 1 mrlee mrlee 0 7月 21 16:41 syscall
dr-xr-xr-x 3 mrlee mrlee 0 7月 21 16:41 task/
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 timers
-rw-rw-rw- 1 mrlee mrlee 0 7月 21 16:41 timerslack_ns
-rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 uid_map
-r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 wchan
mrlee@mrlee-virtual-machine:~/test/c++$
解释:
cwd符号链接的是进程运行目录
exe符号链接的就是程序的结对路径
cmdline:就是程序运行时输入的命令行命令
environ记录程序运行时的环境变量
fd目录下是进程打开或者使用的文件的符号链接