面试复盘四

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

        总共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目录下是进程打开或者使用的文件的符号链接

猜你喜欢

转载自blog.csdn.net/weixin_43155199/article/details/125910583