• 巨人网络笔试题


    1. multiset操作

    这儿是对STL各个容器的相关介绍. http://www.cplusplus.com/reference/stl/

    multiset比较重要的操作就是: insert, erase, count和find

    2. memcpy实现

    void *Memcpy(void *dst, const void *src, size_t size)
    {
        char *psrc;
        char *pdst;
        if(NULL == dst || NULL == src)
        {
            return NULL;
        }
        if((src < dst) && (char *)src + size > (char *)dst) // 自后向前拷贝
        {
            psrc = (char *)src + size - 1;
            pdst = (char *)dst + size - 1;
            while(size--)
            {
                *pdst-- = *psrc--;
            }
        }
        else
        {
            psrc = (char *)src;
            pdst = (char *)dst;
            while(size--)
            {
                *pdst++ = *psrc++;
            }
        }
        return dst;
    }

    3. vector,list,map循环删除

    循环删除vector和map中的元素

    删除所有偶数项,并打印出删除的项

    (1). vector/queue

    正确方法1:

    void erase(vector<int> &v)
    {
        for(vector<int>::iterator vi=v.begin();vi!=v.end();)
        {
            if(*vi % 2 == 0)
            {
                cout << "Erasing " << *vi << endl;
                vi = v.erase(vi);
            }
            else ++vi;
        }
    }

    正确方法2:

    void erase2(vector<int> &v)
    {
        for(vector<int>::reverse_iterator ri=v.rbegin();ri!=v.rend();)
        {
            if(*ri % 2 == 0)
            {
                cout << "Erasing " << *ri << endl;
                v.erase((++ri).base()); //erase()函数期待的是正向iterator,故而这里要调
                //用base()函数将逆向iterator转换为正向的
            }
            else ++ri;
        }
    }

    (2).map/list

    正确方法

    void erase(map<int,int> &m)
    {
        for(map<int,int>::iterator mi=m.begin();mi!=m.end();)
        {
            if(mi->second % 2 == 0)
            {
                cout << "Erasing " << mi->second << endl;
                m.erase(mi++);
            }
            else ++mi;
        }
    }

    4. mysql操作

    5. 静态变量与类大小关系.

    首先,类的大小是什么?确切的说,类只是一个类型定义,它是没有大小可言的。

    用sizeof运算符对一个类型名操作,得到的是具有该类型实体的大小。

    如果

    Class A;

    A obj;

    那么sizeof(A)==sizeof(obj)

    那么sizeof(A)的大小和成员的大小总和是什么关系呢,很简单,一个对象的大小大于等于所有非静态成员大小的总和。

    为什么是大于等于而不是正好相等呢?超出的部分主要有以下两方面:

    1) C++对象模型本身

    对于具有虚函数的类型来说,需要有一个方法为它的实体提供类型信息(RTTI)和虚函数入口,常见的方法是建立一个虚函数入口表,这个表可为相同类型的对象共享,因此对象中需要有一个指向虚函数表的指针,此外,为了支持RTTI,许多编译器都把该类型信息放在虚函数表中。但是,是否必须采用这种实现方法,C++标准没有规定,但是这几乎是主流编译器均采用的一种方案。

    2) 编译器优化

    因为对于大多数CPU来说,CPU字长的整数倍操作起来更快,因此对于这些成员加起来如果不够这个整数倍,有可能编译器会插入多余的内容凑足这个整数倍,此外,有时候相邻的成员之间也有可能因为这个目的被插入空白,这个叫做“补齐”(padding)。所以,C++标准紧紧规定成员的排列按照类定义的顺序,但是不要求在存储器中是紧密排列的。

    基于上述两点,可以说用sizeof对类名操作,得到的结果是该类的对象在存储器中所占据的字节大小,由于静态成员变量不在对象中存储,因此这个结果等于各非静态数据成员(不包括成员函数)的总和加上编译器额外增加的字节。后者依赖于不同的编译器实现,C++标准对此不做任何保证。

    C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。

    如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable,在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。而虚函数本身和其他成员函数一样,是不占用对象的空间的。

    5. 构造函数中使用memset(this,0,sizeof(*this))初始化类.

    memset在c中是用的非常频繁的初始化函数了,当然也被带到了C++当中,因为当有如下类涉及到非常多的成员变量,很多coder经常偷懒改用memset在构造函数当中初始化

    struct Test
    {
    int _1;
    int _2;
    long _3;
    ...
    Test(){memset(this,0,sizeof(Test));}
    };

    以上如果所有成员变量是简单的内置类型是没有问题,但是可能某次需求迫使你需要往Test中增加一个数组,如下

    struct Test
    {
    int _1;
    int _2;
    long _3;
    ...
    std::vector<int> _n;
    Test(){memset(this,0,sizeof(Test));}
    };

    结果在不同的编译器是不同的,最好的情况当然是启动时程序就挂掉了,至于为什么会挂掉,明白vector中实现了什么就知道了,当然不仅仅是vector,其他stl或者自己定制的容器可能都存在这个问题。

    上面是一个陷阱,再一个陷阱就是在派生类的构造函数当中使用memset的问题

    class Base
    {
    int _a;
    };
    class Derive : public Base
    {
    public:
    Derive(){memset(this,0,sizeof(Derive));}
    };

    以上代码看出问题了没?

    如果在改成下面的呢?

    class Base
    {
    int _a;
    public:
    virtual ~Base(){}
    };

    memset做了一件本来你是做不到的事情,那就是把Derive的虚表指针也之位0了,结果当然就是内容泄露了

    总结:

    memset在内存操作方面太灵活了,但是我们也得注意在c++使用的时候是存在很多陷阱的,稍有不慎可能会造成很大的隐患,以上的问题并不是马上就一定会暴露出来的,不同的编译器现象是不一样的

    5. 使用C++写成尽可能多的死循环方式.

    C/C++中三种死循环的写法

    一般来说,我们都会避免在程序中出现死循环。但在某些情况下我们又需要死循环,因此特总结三种常用的死循环的写法如下:

    (1)、

    while (1)

    {

    ........

    }

    (2)、for(;;)

    {

    ....

    }

    (3)、Loop:

    ....

    Goto Loop;

    6. C++类默认生成的函数.

    ISO/IEC 14882(C++的国际标准文件)中说明:

    一个空类必须默认生成四个成员函数:

    构造函数,析构函数,拷贝构造函数,赋值函数

    class Empty {

    public:

    Empty(); // 缺省构造函数

    Empty(const Empty& rhs); // 拷贝构造函数

    ~Empty(); // 析构函数

    Empty& operator=(const Empty& rhs); // 赋值运算符

    };

    有一点争议的是:

    在《effective c++》中,大师说到一个类中应该包含六个默认成员函数,另外两个是

    取址运算符和常取址运算符

    Empty* operator&(); // address-of operators

    const Empty* operator&() const;

    7. 编译过程包括什么,哪些可以本地完成,哪些可以分发出去完成.

    编译过程分为四步:

    一、预处理工作:处理宏定义,不管是由-D参数指定,还是在源码内部通过#define,或者使用了标准库,扩展库中的宏,都会替换为定义的值。

    命令:cpp a.c>a.i 输出的a.i就是预处理过的文件,包含了完整的内容。

    由此可以得出,宏不是运行时的定义内容,也不是编译时的内容,而是预处理阶段就完成的。

    二、编译阶段:将源代码(也就是预处理过的代码)编译为特定机器的汇编语言。

    命令:gcc -S -Wall a.i 输出为特定的汇编内容。

    三、汇编阶段:将汇编源码汇编为机器码内容。

    命令:as a.s -o a.o 此处如果有对外部的函数使用,则会预留未定义的地址,以供最后一步链接来使用。

    四、链接阶段:将链接对象文件(上编译汇编后的文件)链接为可执行文件,此过程比较复杂,需要链接很多外部的库文件,包括静态的,动态的库。

    命令:ld -dynamic-linker .. .. ..

    可以使用gcc a.o 来简化上一过程。

    最后得出的是可执行文件。

    8. TCP/IP过程

    clip_image002

    表1-2  TCP/IP模型各个层次的功能和协议

    层次名称

    功 能

    协 议

    网络接口

    (Host-to-Net Layer)

    负责实际数据的传输,对应OSI参考模型的下两层

    HDLC(高级链路控制协议)

    PPP(点对点协议)

    SLIP(串行线路接口协议)

    网际层

    (Inter-network Layer)

    负责网络间的寻址

    数据传输,对应OSI参考模型的第三层

    IP(网际协议)

    ICMP(网际控制消息协议)

    ARP(地址解析协议)

    RARP(反向地址解析协议)

    传输层

    (Transport Layer)

    负责提供可靠的传输服务,对应OSI参考模型的第四层

    TCP(控制传输协议)

    UDP(用户数据报协议)

    应用层

    (Application Layer)

    负责实现一切与应用程序相关的功能,对应OSI参考模型的上三层

    FTP(文件传输协议)

    HTTP(超文本传输协议)

    DNS(域名服务器协议)

    SMTP(简单邮件传输协议)

    NFS(网络文件系统协议)

    说明  TCP/IP与OSI最大的不同在于OSI是一个理论上的网络通信模型,而TCP/IP则是实际运行的网络协议。

    clip_image003clip_image004

    为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

    这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的 ACK报文和FIN报文多数情况下都是分开发送的。

  • 相关阅读:
    十七、mysql数据库备份
    消费端ACK和重回队列
    RabbitMQ TTL、死信队列
    消费端限流策略
    029异常处理
    028class_part2
    027class_part1
    026json和pickle,xml模块
    025__name__变量和目录结构规范
    024模块的概念
  • 原文地址:https://www.cnblogs.com/xkfz007/p/2763270.html
Copyright © 2020-2023  润新知