• 面试复盘四


            由于前一天孩子打了疫苗然后晚上发烧了,睡觉的时候差不多凌晨四五点了,一晚上睡了两三个小时,在这之前呢,上午十一点面了一家,下午两点半又面试了一家,下午四点半又面试了一家,面试这家的时候是第四家,脑袋属于迷糊状态。

            总共2次技术面试,第一面和第二面恰巧是同一个面试官,问了很多的问题,也聊了很多,虽然自己感觉面试的一塌糊涂,最后面试官给过了,面试官人很不错,2面直接给了offer,很感谢这位面试官。可是因为其他原因,我选择了拒绝,这里不能说是哪家公司,希望这位面试官步步高升。        

            把面试回答不好的地方做以下总结。及时的查漏补缺。

            另外一点总结就是:面试的时候,别管前一个问题回答的如何,下一个题目一定要认真仔细的回答清楚,面试是一个很好的学习机会。

    问题1:找出下面代码的错误,并指明错误原因,并说明printf能否打印

    1. #include
    2. #include
    3. int main(void)
    4. {
    5. char *p = new char[100];
    6. p += 2;
    7. delete []p;
    8. printf("it is demo\n");
    9. }

    运行结果:

    gdb调试: 

    结论:

    printf不能打印。p是一个无效的指针,会出现核心转储

    原因(题目考察的核心点):

    只有 malloc返回的地址才能free()

    new和delete本质也是调用的malloc和free。

    改正:

    1. #include
    2. #include
    3. int main(void)
    4. {
    5. char *p = new char[100];
    6. //p += 2;
    7. delete []p;
    8. printf("it is demo\n");
    9. }

     问题2:下面代码的printf能否打印?错误在哪?

    1. #include
    2. #include
    3. struct MyClass{
    4. int a;
    5. };
    6. int main(void)
    7. {
    8. char *p = new char[100];
    9. delete p;
    10. printf("it is test demo1\n");
    11. MyClass* q = new MyClass [100];
    12. delete q;
    13. printf("It is a test demo2!\n");
    14. }

    其实这个问题很简单:

     (1)printf可以正常打印

     (2)会出现内存泄漏

    问题3:下一段代码能否正常打印?

    1. #include
    2. #include
    3. int main(void)
    4. {
    5. char* ptr = "0123456789";
    6. while(*ptr)
    7. {
    8. printf("%c\n", *ptr++);
    9. }
    10. return 0;
    11. }

    结论:这段代码可以正常打印如下结果:

     gdb调试程序也是正常退出

    问题四:下段代码输出什么?

    1. #include
    2. #include
    3. int main(void)
    4. {
    5. char* ptr = "0123456789";
    6. while(*ptr++)
    7. {
    8. printf("%c\n", *ptr);
    9. }
    10. return 0;
    11. }

    结论:

     为什么会多一个换行呢?

    原因:最后的'\0'也会被计算到里面,所以最后多了一个换行

    考察的主要问题是:*和++运算符的优先级,*的优先级高于++的优先级,那这个问题就很好理解了

     问题5:下段代码输出什么?

    1. #include
    2. #include
    3. using namespace std;
    4. int main(void)
    5. {
    6. unsigned char aaa[100] = {0};
    7. int bbb[200] = {0};
    8. cout << sizeof(aaa) << "," << sizeof(bbb) << endl;
    9. int var,count,incr;
    10. (count=0, incr=10, count++) ? var = (incr-=2, count+2) : var = (incr--, count++);
    11. cout << incr << "," << var << endl;
    12. return 0;
    13. }

    分析:

    (1)前半段呢,都没问题

     (2)后半段呢,是个三目描述符,首先我第一次见到这种三目,当时有点木,真的,后来仔细分析了如下:
    1)(count=0, incr=10, count++):主要是判断这个表达式是不是为真。这两个数字在三目判断的时候分别为:incr=10, count=0,所以逻辑判断为假。

    2)逻辑判断为假,执行 var = (incr--, count++), 这个的意思是:执行括号里面的操作,

    执行结果为:

     问题6:下段代码输出什么?

    1. #include
    2. #include
    3. using namespace std;
    4. int func(int x) {
    5. int count = 0;
    6. while(x)
    7. {
    8. count++;
    9. x = x&(x-1);
    10. }
    11. return count;
    12. }
    13. int main(void)
    14. {
    15. cout << " func(255) = " << func(255) << endl ;
    16. return 0;
    17. }

    唉,其实这个是一个按位运算

    1. Starting program: /home/mrlee/test/c++/a.out
    2. Breakpoint 1, main () at main.cpp:20
    3. 20 cout << " func(255) = " << func(255) << endl ;
    4. (gdb) s
    5. func (x=255) at main.cpp:9
    6. 9 int count = 0;
    7. (gdb) n
    8. 10 while(x)
    9. (gdb) wathc x
    10. Undefined command: "wathc". Try "help".
    11. (gdb) watch x
    12. Hardware watchpoint 2: x
    13. (gdb) watch count
    14. Hardware watchpoint 3: count
    15. (gdb) n
    16. 12 count++;
    17. (gdb) c
    18. Continuing.
    19. Hardware watchpoint 3: count
    20. Old value = 0
    21. New value = 1
    22. func (x=255) at main.cpp:13
    23. 13 x = x&(x-1);
    24. (gdb) c
    25. Continuing.
    26. Hardware watchpoint 2: x
    27. Old value = 255
    28. New value = 254
    29. func (x=254) at main.cpp:10
    30. 10 while(x)
    31. (gdb) c
    32. Continuing.
    33. Hardware watchpoint 3: count
    34. Old value = 1
    35. New value = 2
    36. func (x=254) at main.cpp:13
    37. 13 x = x&(x-1);
    38. (gdb) c
    39. Continuing.
    40. Hardware watchpoint 2: x
    41. Old value = 254
    42. New value = 252
    43. func (x=252) at main.cpp:10
    44. 10 while(x)
    45. (gdb) c
    46. Continuing.
    47. Hardware watchpoint 3: count
    48. Old value = 2
    49. New value = 3
    50. func (x=252) at main.cpp:13
    51. 13 x = x&(x-1);
    52. (gdb) c
    53. Continuing.
    54. Hardware watchpoint 2: x
    55. Old value = 252
    56. New value = 248
    57. func (x=248) at main.cpp:10
    58. 10 while(x)
    59. (gdb) c
    60. Continuing.
    61. Hardware watchpoint 3: count
    62. Old value = 3
    63. New value = 4
    64. func (x=248) at main.cpp:13
    65. 13 x = x&(x-1);
    66. (gdb) c
    67. Continuing.
    68. Hardware watchpoint 2: x
    69. Old value = 248
    70. New value = 240
    71. func (x=240) at main.cpp:10
    72. 10 while(x)
    73. (gdb) c
    74. Continuing.
    75. Hardware watchpoint 3: count
    76. Old value = 4
    77. New value = 5
    78. func (x=240) at main.cpp:13
    79. 13 x = x&(x-1);
    80. (gdb) c
    81. Continuing.
    82. Hardware watchpoint 2: x
    83. Old value = 240
    84. New value = 224
    85. func (x=224) at main.cpp:10
    86. 10 while(x)
    87. (gdb) c
    88. Continuing.
    89. Hardware watchpoint 3: count
    90. Old value = 5
    91. New value = 6
    92. func (x=224) at main.cpp:13
    93. 13 x = x&(x-1);
    94. (gdb) c
    95. Continuing.
    96. Hardware watchpoint 2: x
    97. Old value = 224
    98. New value = 192
    99. func (x=192) at main.cpp:10
    100. 10 while(x)
    101. (gdb) c
    102. Continuing.
    103. Hardware watchpoint 3: count
    104. Old value = 6
    105. New value = 7
    106. func (x=192) at main.cpp:13
    107. 13 x = x&(x-1);
    108. (gdb) c
    109. Continuing.
    110. Hardware watchpoint 2: x
    111. Old value = 192
    112. New value = 128
    113. func (x=128) at main.cpp:10
    114. 10 while(x)
    115. (gdb) c
    116. Continuing.
    117. Hardware watchpoint 3: count
    118. Old value = 7
    119. New value = 8
    120. func (x=128) at main.cpp:13
    121. 13 x = x&(x-1);
    122. (gdb) c
    123. Continuing.
    124. Hardware watchpoint 2: x
    125. Old value = 128
    126. New value = 0
    127. func (x=0) at main.cpp:10
    128. 10 while(x)
    129. (gdb) c
    130. Continuing.
    131. Watchpoint 2 deleted because the program has left the block in
    132. which its expression is valid.
    133. Watchpoint 3 deleted because the program has left the block in
    134. which its expression is valid.
    135. 0x00000000004008d1 in main () at main.cpp:20
    136. 20 cout << " func(255) = " << func(255) << endl ;
    137. (gdb) p func(255)
    138. $1 = 8
    139. (gdb)

    返回8

    个人觉得最好是手画一画,其实这个就是255最大是8个位全为1,没减1,向前一位。

    1. 1111 1111
    2. & count=1
    3. 1111 1110
    4. x = 254
    5. 1111 1110
    6. & count=2
    7. 1111 1101
    8. x = 252
    9. 1111 1100
    10. & count=3
    11. 1111 1011
    12. x = 248
    13. 1111 1000
    14. & count =4
    15. 1111 0111
    16. x=240
    17. 1111 0000
    18. & count=5
    19. 1110 1111
    20. x=224
    21. 1110 0000
    22. & count=6
    23. 1101 1111
    24. x =192
    25. 1100 0000
    26. & count=7
    27. 1011 1111
    28. x=128
    29. 1000 0000
    30. & count=8
    31. 1111 1111
    32. x=0;

    问题7:下面输出是啥?

    1. map<int, string> mapStudent;
    2. map<int, string>::iterator iter;
    3. mapStudent.insert(map<int, string>::value_type (1, "student_one"));
    4. mapStudent.insert(map<int, string>::value_type (1, "student_two"));
    5. for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
    6. cout<first<<' '<second<
    7. mapStudent[1] = "student_three";
    8. mapStudent[1] = "student_four";
    9. for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
    10. cout<first<<' '<second<
    11. for(int i = 1; i < 3; i++)
    12. cout<< mapStudent[i] <
    13. cout<< mapStudent.size() << endl;

     问题8:下面代码是否有问题?输出是?

    1. vectordemo{0,1,2,3,4,5,6,7,8,9,10 };
    2. vector::iterator it = demo.begin();
    3. for(it = demo.begin(); it != demo.end(); it++)
    4. {
    5. if(!(*it%2))
    6. demo.erase(it);
    7. }
    8. for(it = demo.begin(); it != demo.end(); it++)
    9. cout << *it << endl;

    问题9:下面代码有没有问题?        

    1. struct ComplexNumber
    2. {
    3. int Real_Quantity;
    4. int Imaginary_Quantity;
    5. };
    6. multiset 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.获取进程的工作目录

    1. mrlee@mrlee-virtual-machine:~/test/c++$ ll /proc/7489
    2. 总用量 0
    3. dr-xr-xr-x 9 mrlee mrlee 0 721 16:39 ./
    4. dr-xr-xr-x 262 root root 0 721 11:58 ../
    5. dr-xr-xr-x 2 mrlee mrlee 0 721 16:41 attr/
    6. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 autogroup
    7. -r-------- 1 mrlee mrlee 0 7月 21 16:41 auxv
    8. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 cgroup
    9. --w------- 1 mrlee mrlee 0 7月 21 16:41 clear_refs
    10. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:39 cmdline
    11. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 comm
    12. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 coredump_filter
    13. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 cpuset
    14. lrwxrwxrwx 1 mrlee mrlee 0 721 16:41 cwd -> /home/mrlee/test/c++/
    15. -r-------- 1 mrlee mrlee 0 7月 21 16:41 environ
    16. lrwxrwxrwx 1 mrlee mrlee 0 721 16:41 exe -> /home/mrlee/test/c++/a.out*
    17. dr-x------ 2 mrlee mrlee 0 7月 21 16:39 fd/
    18. dr-x------ 2 mrlee mrlee 0 7月 21 16:41 fdinfo/
    19. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 gid_map
    20. -r-------- 1 mrlee mrlee 0 7月 21 16:40 io
    21. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 limits
    22. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 loginuid
    23. dr-x------ 2 mrlee mrlee 0 7月 21 16:41 map_files/
    24. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 maps
    25. -rw------- 1 mrlee mrlee 0 7月 21 16:41 mem
    26. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 mountinfo
    27. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 mounts
    28. -r-------- 1 mrlee mrlee 0 7月 21 16:41 mountstats
    29. dr-xr-xr-x 5 mrlee mrlee 0 721 16:41 net/
    30. dr-x--x--x 2 mrlee mrlee 0 7月 21 16:41 ns/
    31. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 numa_maps
    32. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 oom_adj
    33. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 oom_score
    34. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 oom_score_adj
    35. -r-------- 1 mrlee mrlee 0 7月 21 16:41 pagemap
    36. -r-------- 1 mrlee mrlee 0 7月 21 16:41 patch_state
    37. -r-------- 1 mrlee mrlee 0 7月 21 16:41 personality
    38. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 projid_map
    39. lrwxrwxrwx 1 mrlee mrlee 0 721 16:41 root -> //
    40. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 sched
    41. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 schedstat
    42. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 sessionid
    43. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 setgroups
    44. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 smaps
    45. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 smaps_rollup
    46. -r-------- 1 mrlee mrlee 0 7月 21 16:41 stack
    47. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:39 stat
    48. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 statm
    49. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:39 status
    50. -r-------- 1 mrlee mrlee 0 7月 21 16:41 syscall
    51. dr-xr-xr-x 3 mrlee mrlee 0 721 16:41 task/
    52. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 timers
    53. -rw-rw-rw- 1 mrlee mrlee 0 721 16:41 timerslack_ns
    54. -rw-r--r-- 1 mrlee mrlee 0 7月 21 16:41 uid_map
    55. -r--r--r-- 1 mrlee mrlee 0 7月 21 16:41 wchan
    56. mrlee@mrlee-virtual-machine:~/test/c++$

    解释:

            cwd符号链接的是进程运行目录

            exe符号链接的就是程序的结对路径

            cmdline:就是程序运行时输入的命令行命令

            environ记录程序运行时的环境变量

            fd目录下是进程打开或者使用的文件的符号链接

  • 相关阅读:
    训练专门化的大型语言模型(LLM)现在更简单了
    K8S-高可用集群
    2023全新云渲染测评!效果图渲染哪个平台性价比更高?
    概念题总结
    数据结构应用大全
    element UI table横向树结合checkbox进行多选,实现各个节点的[全选,半选,不选]状态附带模拟数据
    rust闭包
    Go语言网络编程(socket编程)UDP
    Python入门基础知识新手必备(赶紧收藏)
    2020年上半年软件设计师真题
  • 原文地址:https://blog.csdn.net/weixin_43155199/article/details/125910583