百度重点面试题 - Go语言中文社区

百度重点面试题


C/C++程序的内存分区

1)、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 
操作方式类似于数据结构中的栈。 
2)、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回 
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 
3)、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的 
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另 
一块区域。 - 程序结束后由系统释放。 
4)、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 
5)、程序代码区—存放函数体的二进制代码。 

栈区与堆区的区别:

栈区与堆区的区别:
1)堆和栈中的存储内容:栈存局部变量、函数参数等。堆存储使用new、malloc申请的变量等;
2)申请方式:栈内存由系统分配,堆内存由自己申请;
3)申请后系统的响应:栈——只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆——首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表 中删除,并将该结点的空间分配给程序;
4)申请大小的限制:Windows下栈的大小一般是2M,堆的容量较大;
5)申请效率的比较:栈由系统自动分配,速度较快。堆使用new、malloc等分配,较慢;
总结:栈区优势在处理效率,堆区优势在于灵活;

IO模型——IO多路复用机制

下面开始介绍IO多路复用:

(1)I/O多路复用技术通过把多个I/O的阻塞复用到同一个select、poll或epoll的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程。

(2)select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

(3)I/O多路复用的主要应用场景如下:

服务器需要同时处理多个处于监听状态或者多个连接状态的套接字;
服务器需要同时处理多种网络协议的套接字;

(4)目前支持I/O多路复用的系统调用有 select,poll,epoll,epoll与select的原理比较类似,但epoll作了很多重大改进,现总结如下:

①支持一个进程打开的文件句柄FD个数不受限制(为什么select的句柄数量受限制:select使用位域的方式来传递关心的文件描述符,因为位域就有最大长度,在Linux下是1024,所以有数量限制);

②I/O效率不会随着FD数目的增加而线性下降;

③epoll的API更加简单;

三种接口调用介绍

①select函数调用格式:

#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)

②poll函数调用格式:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout)

③epoll函数格式(操作过程包括三个函数):

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

(6)作用:一定程度上替代多线程/多进程,减少资源占用,保证系统运行的高效率;

常用的Linux命令

答:(1)查看CPU利用率:top

(2)查看当前目录:pwd和ls(ls -a可以查看隐藏目录)

(3)切换目录:cd

(4)查看文件占用磁盘大小:du和df

(5)创建文件夹:mkdir

(6)新建文件:touch

(7)查看文件:cat

(8)拷贝:cp 移动:mv 删除:rm

(9)查看进程:ps,如ps aux

(10)删除进程:kill -9 PID,注-9是参数

(11)程序运行时间:time,使用时在命令前添加time即可,如:time ./test,可得到三个时间:real 0m0.020s,user 0m0.000s,sys 0m0.018s

grep命令(重要的常用命令之一):常用于打开文本修改保存,类似打windows开开TXT文本并修改;

sed命令(常用重要命令之一):主要用于对文件的增删改查;

awk命令(重要常用命令之一):取列是其擅长的;

find 命令(常与xargs命令配合):查找 -type 文件类型-name 按名称查找-exec执行命令;

xargs命令:配合find/ls查找,将查找结果一条条的交给后续命令处理;

gdb调试工具:

要调试C/C++的程序,一般有如下几个步骤:

①首先在编译时,我们必须要把调试信息加到可执行文件中,编译生成可执行文件——-> g++ -g hello.cpp -o hello;

②启动GDB编译hello程序———-> gdb hello;

③显示源码————> l;

④开始调试:break 16——设置断点在16行,break func——设置断点在函数func()入口处,info break——查看断点信息,n——单步运行,c——继续运行程序,r——运行程序;p i——打印i的值,finish——退出程序,q——退出gdb。

new/delete、malloc/free底层实现原理:

概述:new/delete的底层实现是调用malloc/free函数实现的,而malloc/free的底层实现也不是直接操作内存而是调用系统API实现的。

这里写图片描述
这里写图片描述

注意,针对上图最末尾所述的“new[]/delete[]时会多开辟4字节用于存储对象个数”,作如下说明:
①对于内置类型:
new []不会在首地址前4个字节定义数组长度。
delete 和 delete[]是一样的执行效果,都会删除整个数组,要删除的长度从new时即可知道。
②对于自定义类型:
new []会在首地址前4个字节定义数组长度。
当delete[]时,会根据前4个字节所定义的长度来执行析构函数删除整个数组。
如果只是delete数组首地址,只会删除第一个对象的值。

overload、override、overwrite的介绍

答:(1)overload(重载),即函数重载:
①在同一个类中;
②函数名字相同;
③函数参数不同(类型不同、数量不同,两者满足其一即可);
④不以返回值类型不同作为函数重载的条件。
(2)override(覆盖,子类改写父类的虚函数),用于实现C++中多态:
①分别位于父类和子类中;
②子类改写父类中的virtual方法;
③与父类中的函数原型相同。
(3)overwrite(重写或叫隐藏,子类改写父类的非虚函数,从而屏蔽父类函数):
①与overload类似,但是范围不同,是子类改写父类;
②与override类似,但是父类中的方法不是虚函数。

多线程

    #include <iostream>
    #include <unistd.h>
    #include <pthread.h>
    using namespace std;

    void *thread(void *ptr)
    {
        for(int i = 0;i < 3;i++) {
            sleep(1);
            cout << "This is a pthread." << endl;
        }
        return 0;
    }

    int main() {
        pthread_t id;
        int ret = pthread_create(&id, NULL, thread, NULL);//创建线程
        if(ret) {
            cout << "Create pthread error!" << endl;
            return 1;
        }
        for(int i = 0;i < 3;i++) {
            cout <<  "This is the main process." << endl;
            sleep(1);
        }
        pthread_join(id, NULL);//等待线程结束
        return 0;
    }

多线程的主要优点包括:

(1)多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态;

(2)占用大量处理时间的任务使用多线程可以提高CPU利用率,即占用大量处理时间的任务可以定期将处理器时间让给其它任务;

(3)多线程可以分别设置优先级以优化性能。

以下是最适合采用多线程处理:

(1)耗时或大量占用处理器的任务阻塞用户界面操作;

(2)各个任务必须等待外部资源 (如远程文件或 Internet连接)。

多线程的主要缺点包括:

(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。

(2)对线程进行管理要求额外的 CPU开销,线程的使用会给系统带来上下文切换的额外负担。

(3)线程的死锁。即对共享资源加锁实现同步的过程中可能会死锁。

(4)对公有变量的同时读或写,可能对造成脏读等;

长连接与短连接

答:(1)就是TCP长连接和TCP短连接:

①TCP长连接:TCP长连接指建立连接后保持连接而不断开。若一段时间内没有数据传输,服务器会发送心跳包给客户端,判断客户端是否还在线,叫做TCP长连接中的keep alive。一般步骤:连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接;

②TCP短连接:指连接建立并传输数据完成后,就断开连接。一般步骤:连接→数据传输→关闭连接;

③使用场景:长连接适合单对单通信且连接数不太多的情况;短连接适合连接数多且经常更换连接对象的;

HTTP是什么连接:

①在HTTP/1.0中,默认使用的是短连接。但从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:
②http长连接并不是一直保持连接

http的长连接也不会是永久保持连接,它有一个保持时间如20s(从上一次数据传输完成开始计时),可以在不同的服务器软件(如Apache)中设定这个时间,若超过该时间限制仍然无数据通信传输,服务器就主动关闭该连接。注:实现长连接要客户端和服务端都支持长连接。

③http连接实质:http的长连接/短连接实质上就是TCP的长/短连接。

class与struct的区别

答:C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能:
①struct能包含成员函数吗? 能!
②struct能继承吗? 能!!
③struct能实现多态吗? 能!!!

既然这些它都能实现,那它和class还能有什么区别?

最本质的一个区别就是成员默认属性和默认继承权限的不同:

①若不指明,struct成员的默认属性是public的,class成员的默认属性是private的;

②若不指明,struct成员的默认继承权限是public的,class成员的默认继承权限是private的;

C++多态性(包含多态)注意点

    #include <iostream> 
    using namespace std; 
    class A 
    { 
        public: 
        virtual void out(int i = 1) 
        { 
            cout << "class A " << i <<endl; 
        } 
    }; 
    class B : public A 
    { 
        public: 
        virtual void out(int i = 2) 
        { 
            cout <<"class B " <<i <<endl; 
        } 
    }; 
    int main() 
    { 
        A a; 
        B b; 
        A * p = &a; 
        p->out(); 
        p->out(3); 
        p = &b; 
        p->out(); 
        p->out(4); 
        B * p1 = &b; 
        p1->out(); 
        p1->out(5); 
        return 0; 
    }

缺省参数是静态绑定的,pb->out()时,pb的静态类型是A*,它的缺省参数是1;但是调用的是B::out(int i);
这里写图片描述

一个类中将所有的成员函数都尽可能地设置为虚函数总是有益的,但以下不可以设置为虚函数:
①只有类的成员函数才能说明为虚函数;
②静态成员函数不能是虚函数(虚函数是动态绑定的,静态函数必然不可);
③内联函数不能为虚函数(虚函数在调用中需要从虚函数表中取地址的,而内联函数是没有指定地址的);
④构造函数不能是虚函数(虚函数表是在构造函数运行时初始化(给虚函数分配地址)的,若构造函数是虚函数,那么就会出现自己在运行时才给自己分配地址,显然不可);

析构函数通常声明为虚函数

因为多态(即基类对象指向派生类)情况下,若析构函数是虚函数,则对象在释放时会首先调用派生类继承的析构函数,然后再调用基类的析构函数,实现两者的同时释放。若析构函数不是虚函数,多态下对象是释放时就只会调用基类的析构函数,而造成派生类对象未释放而内存泄漏。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_35433716/article/details/82103000
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-08-27 10:31:11
  • 阅读 ( 1471 )
  • 分类:面试题

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢