资料整理

目录

1.C++、面向对象OOP、设计模式DP

1.1语言

1.1.1.C++的转型操作:static_cast、dynamic_cast、const_cast、reinterpret_cast

1.1.2.成员函数可以同时修饰为static和const吗?不可以!!!

1.1.3纯虚函数可以有函数体吗?当然可以!

1.1.4 static函数可以调用非static函数吗?反之呢?概念不清造成这些愚蠢的问题!

1.1.5 const函数可以调用非const函数吗?不可以!反之呢?

1.1.6类似的问题,const对象就就只能调用const成员函数吗?是的!

1.1.7Effective C++ item9:绝不要在构造函数和析构函数种调用virtual函数!

2.数据结构与算法

3.操作系统OS

1.进程/线程

1.1系统运行时,同时为进程和线程分配内存吗?不是

1.2内存的分配方式的分配方式有几种?

1.3进程和线程的区别(从拥有资源、调度、并发、系统开销方面区别)

1.4进程间通信方式

4.计算机网络

1.TCP/UDP

1.TCP连接建立和释放(三次握手四次挥手)

2.TCP/IP协议栈与层次

3.tcp断开时time_wait状态发生在哪一端?主动断开方

4.TCP的SYN数据包中的源IP地址可以伪造吗?不可以!

TCP 不能伪造源 IP 地址是因为 TCP 协议中的三次握手的存在,如果源 IP 地址被修改,那么三次握手将无法达成。

TCP面向连接,三次握手后,源头IP地址一点是真实的!

UDP不是面向连接的,所以源IP地址可以伪造!

3.在浏览器中输入URL到显示网页是一个什么样的过程,使用了哪些协议?

4.一台刚刚接入互联网的WEB服务器第一次被访问到时,不同协议的发生顺序是:

5.ICMP报文和ping命令

5.1 禁止ping就是禁止ICMP协议吗?

6.网页不能访问,但QQ正常。是什么情况?

5.数据库

1.数据库事务四大特性(简称ACID) 

2.数据库的索引

6.计算机系统(硬件、汇编)(计算机组成原理、微机原理与接口)


1.C++、面向对象OOP、设计模式DP

1.1语言

1.1.1.C++的转型操作:static_cast、dynamic_cast、const_cast、reinterpret_cast

问题:类层次结果中,基类要有虚函数,才能使用dynamic_cast吗?是的!

需要注意的是如果基类中不含虚函数,dynamic_cast 下行转换编译会出错:

在类层次间进行上行转换时,dynamic_cast和static_cast 的效果是一样的;

在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast 更安全。

参考:https://www.cnblogs.com/mfrbuaa/p/4590183.html

https://www.cnblogs.com/TenosDoIt/p/3175217.html

https://www.cnblogs.com/chenyangchun/p/6795923.html

问题:static_cast对于下行转换安全吗?不安全:static_cast下行转换是不安全的!

问题:关于转型的合法性检查,dynamic_cast是在运行期间,而const_cast是在编译期间吗?是的!

作为四个内部类型转换操作符之一的dynamic_cast和传统的C风格的强制类型转换有着巨大的差别。除了dynamic_cast以外的转换,其行为的都是在编译期就得以确定的,转换是否成功,并不依赖被转换的对象。而dynamic_cast则不然,dynamic_cast是在运行期间检查转换的合法性的!。在这里,不再讨论其他三种转换和C风格的转换。

首先,dynamic_cast依赖于RTTI信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查
先看RTTI相关部分,通常,许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型, 这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针.而这种转换其实并不需要dynamic_cast参与.
也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的.

下面看一个例子:

struct B1{
    virtual ~B1(){}
};
struct B2{
    virtual ~B2(){}
};
struct D1 : B1, B2{};
int main()
{
    D1 d;
    B1* pb1 = &d;
    B2* pb2 = dynamic_cast<B2*>(pb1);//L1
    //B2* pb22 = static_cast<B2*>(pb1);  //L2  //错误:类型转换无效!
    return 0;
}

上述定义中可以看到,B1和B2是不相关的类,从L1可以看到,dynamic_cast允许这种转换:只要B1存在多态方法.
L2将编译失败,static_cast并不允许两个完全不相干的类互相转换.

dynamic_cast的这种特性,在提取一个对象的某个接口的时候,非常有用,它很类似于实现了COM的QueryInterface的功能。

参考:https://blog.csdn.net/wingfiring/article/details/633033

 

1.1.2.成员函数可以同时修饰为static和const吗?不可以!!!

答案是不可以。C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的。

我们也可以这样理解:两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。

 

1.1.3纯虚函数可以有函数体吗?当然可以!

可以有,但是没必要这样做,好得设计不会这样设计的。这只是概念问题。知道可以就可以了
1L说的不确切。纯虚函数添加函数体,依旧为纯虚函数,类依旧是抽象类,不能实例化
2L说的很明确,既然纯虚函数所在的类为抽象,那么为纯虚函数添加函数体是完全没有必要的。可以有函数体,但是这不是一种好得设计方式。既然是公共接口,添加函数体多此一举。

1.1.4 static函数可以调用非static函数吗?反之呢?概念不清造成这些愚蠢的问题!

普通成员函数都有一个隐藏的this指针,而静态成员函数没有。

因为这个原因静态成员函数不可以调用非静态成员函数静态只能访问静态,非静态可以访问静态。

1.1.5 const函数可以调用非const函数吗?不可以!反之呢?

const函数就是为了不改变对象的状态!所以const函数当然不能调用非const函数!

1.1.6类似的问题,const对象就就只能调用const成员函数吗?是的!

在C++中只有被声明为const的成员函数才能被一个const类对象调用。

  • const成员函数可以访问非const对象的非const数据成员,const数据成员,也可以访问const对象内的所有数据成员;
  • 非const成员函数只可以访问非const对象的任意的数据成员(不能访问const对象的任意数据成员);

 

1.1.7Effective C++ item9:绝不要在构造函数和析构函数种调用virtual函数!

Never call virtual functions during construction and destruction!

2.数据结构与算法

 

3.操作系统OS

1.进程/线程

1.1系统运行时,同时为进程和线程分配内存吗?不是

1.2内存的分配方式的分配方式有几种?

 1. 从静态存储区分配:此时的内存在程序编译的时候已经分配好,并且在程序的整个运行期间都存在。全局变量,static变量等在此存储。
2. 在栈区分配:相关代码执行时创建,执行结束时被自动释放。局部变量在此存储。栈内存分配运算内置于处理器的指令集中,效率高,但容量有限。
3. 在堆区分配:动态分配内存。用new/malloc时开辟,delete/free时释放。生存期由用户指定,灵活。但有内存泄露等问题。

1.3进程和线程的区别(从拥有资源、调度、并发、系统开销方面区别)

线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行。
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销

1.4进程间通信方式

1)管道:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程之间使用。进程的亲缘关系通常是指父子进程关系。
2)有名管道(FIFO):有名管道也是半双工的通信方式,但是允许在没有亲缘关系的进程之间使用,管道是先进先出的通信方式。
3)信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4)消息队列:消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5)信号 ( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6)共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
7)套接字( socket ) :套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

 

4.计算机网络

1.TCP/UDP

1.TCP连接建立和释放(三次握手四次挥手)

https://blog.csdn.net/m0_37357063/article/details/81866353

2.TCP/IP协议栈与层次

https://blog.csdn.net/m0_37357063/article/details/80657591

3.tcp断开时time_wait状态发生在哪一端?主动断开方

 https://blog.csdn.net/zj6257/article/details/78655642


4.TCP的SYN数据包中的源IP地址可以伪造吗?不可以!

TCP 不能伪造源 IP 地址是因为 TCP 协议中的三次握手的存在,如果源 IP 地址被修改,那么三次握手将无法达成。

TCP面向连接,三次握手后,源头IP地址一点是真实的!

UDP不是面向连接的,所以源IP地址可以伪造!

通过上面的实验以及实验说明,我们可以得出如下几条结论: 
1. 使用基于 TCP 协议的通信不可以对源 IP 地址进行伪造 
2. 使用基于 UDP 协议的通信可以对源 IP 地址进行伪造

TCP 不能伪造源 IP 地址是因为 TCP 协议中的三次握手的存在,如果源 IP 地址被修改,那么三次握手将无法达成。而 UDP 则不同,UDP 中不存在三次握手,那么发送端就只要发送数据即可,而接收端只要接收数据即可。所以,在 TCP 中不能对源 IP 地址进行伪造,而 UDP 中则可以。

参考:https://blog.csdn.net/lemon_tree12138/article/details/51198116

 

参考:https://www.52pojie.cn/forum.php?mod=viewthread&tid=585129&page=1

下面的代码实现的功能是:原始套接字伪造A的IP地址给B发送TCP数据包(类似三次握手第一步)
下面代码中setsockopt这个函数一直报错,请各位指正,参考书是刘文涛的《网络安全编程技术与实例》、李瑞民的《网络扫描技术揭秘》以及诸位大佬的相关代码额。。。。。

#include "stdafx.h"
#include <WinSock2.h>
#pragma warning(disable:4996)        
#include <stdio.h>
#include <string.h>
#include <WS2tcpip.h>
#include "mstcpip.h"                
#include <time.h>
#pragma comment(lib,"ws2_32.lib")
 
#define MAXSIZE 1024
char  *CHAR_SOURCE_IP = "10.251.93.11";                        //所伪造的IP地址
char  *CHAR_TARGET_IP = "10.252.245.38";                        //目标的IP地址
 
//重定义IP数据包头部
typedef struct ip_header
{
        unsigned char h_verlen;         //版本号+头长度--------------1组
        unsigned char tos;                                 //分区服务(默认为0)--------1组
        unsigned short total_len;                //首部及数据总长度-----------2组
        unsigned short ident;                         //分片标识-------------------2组
        unsigned short frag_and_flags;  //分片设置3bit---------------3/4组
 
        unsigned char ttl;                                 //生存周期(跳数限制)-------1组
        unsigned char proto;                         //携带的协议-----------------1组
        unsigned short checksum;                //首部校验和-----------------2组
        unsigned int sourceIP;                         //源IP地址-------------------4组
        unsigned int destIP;                         //目的IP地址-----------------4组
};
 
//重定义TCP数据包首部
typedef struct tcp_header
{
        USHORT th_sport;                                                //16位源端口 
        USHORT th_dport;                                                //16位目的端口 
        unsigned int th_seq;                                        //32位序列号seq
        unsigned int th_ack;                                        //32位确认号         
        unsigned char th_lenres;                                //4位首部长度/6位保留字 
        unsigned char th_flag;                                //6位标志位 
        USHORT th_win;                                                //16位窗口大小 
        USHORT th_sum;                                                //16位校验和 
        USHORT th_urp;                                                //16位紧急数据偏移量 
};
 
//定义TCP伪首部
typedef struct psd_tcp_header
{
        unsigned long saddr; //源地址 
        unsigned long daddr; //目的地址 
        char mbz; char ptcl; //协议类型 
        unsigned short tcpl; //TCP长度 
};
 
//计算首部校验和的函数
unsigned short checksum(unsigned short * buffer, int size)
{
        unsigned long cksum = 0;
        unsigned short answer = 0;
        while (size > 1)
        {
                cksum += *buffer++;                                                                        
                size -= sizeof(USHORT);
        }
        if (size == 1)
        {
                *(char *)&answer = *(char *)buffer;
                cksum += answer;
        }
        while (cksum >> 16)
                cksum = (cksum >> 16) + (cksum & 0xffff);
        return (USHORT)(~cksum);
};
 
 
int attacker2target(int port)
{
        SOCKET sendsocket;
        SOCKADDR_IN addr_in;
        WSADATA wsa;
        ip_header IP_HEADER;
        tcp_header TCP_HEADER;
        psd_tcp_header PSD_TCP_HEADER;
        int SOURCE_PORT = 80;
        char szSendBuf[MAXSIZE] = { 0 };
        unsigned long desip = inet_addr(CHAR_TARGET_IP);
 
        if (WSAStartup(MAKEWORD(2, 2), &wsa) == SOCKET_ERROR)
        {
                printf("error !\n%d\n", GetLastError());
                return false;
        }
 
        srand((unsigned)time(NULL));                //随机种子生成,留出时间
 
        if ((sendsocket = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
        {
                printf("WSASocket failed with error !\n%d\n", GetLastError());
                return false;
        }
         
        bool bOpt = true;
        if (setsockopt(sendsocket, IPPROTO_IP, IP_HDRINCL, (char *)&bOpt, sizeof(bOpt)) == SOCKET_ERROR )
        {
                printf("setsocketopt error !\n %d \n", WSAGetLastError());
                return false;
        }
 
        int nTimeOver = 1000;
        if (setsockopt(sendsocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver, sizeof(nTimeOver)) == SOCKET_ERROR)
        {
                printf("setsockopt failed with error %d\n\n", WSAGetLastError());
                return false;
        }
 
        addr_in.sin_family = AF_INET;
        addr_in.sin_port = htons(port);
        addr_in.sin_addr.S_un.S_addr = inet_addr(CHAR_TARGET_IP);
 
        //填充IP报头
        IP_HEADER.h_verlen = (4 << 4 | sizeof(IP_HEADER) / sizeof(unsigned long));
        // IP_HEADER.tos=0;
        IP_HEADER.total_len = htons(sizeof(IP_HEADER) + sizeof(TCP_HEADER));
        IP_HEADER.ident = 1;
        IP_HEADER.frag_and_flags = 0;
        IP_HEADER.ttl = 128;
        IP_HEADER.proto = IPPROTO_TCP;
        IP_HEADER.checksum = 0;
        IP_HEADER.sourceIP = inet_addr("localhost");
        IP_HEADER.destIP = desip;
 
        //填充TCP报头
        TCP_HEADER.th_dport = htons(port);
        TCP_HEADER.th_sport = htons(SOURCE_PORT); //源端口号
        TCP_HEADER.th_seq = htonl(0x12345678);
        TCP_HEADER.th_ack = 0;
        TCP_HEADER.th_lenres = (sizeof(TCP_HEADER) / 4 << 4 | 0);
        TCP_HEADER.th_flag = 2; //标志位探测,2是SYN 
        TCP_HEADER.th_win = htons(512);
        TCP_HEADER.th_urp = 0;
        TCP_HEADER.th_sum = 0;
 
        PSD_TCP_HEADER.saddr = IP_HEADER.sourceIP;
        PSD_TCP_HEADER.daddr = IP_HEADER.destIP;
        PSD_TCP_HEADER.mbz = 0;
        PSD_TCP_HEADER.ptcl = IPPROTO_TCP;
        PSD_TCP_HEADER.tcpl = htons(sizeof(TCP_HEADER));
 
        memcpy(szSendBuf, &PSD_TCP_HEADER, sizeof(PSD_TCP_HEADER));
        memcpy(szSendBuf + sizeof(PSD_TCP_HEADER), &TCP_HEADER, sizeof(TCP_HEADER));
        TCP_HEADER.th_sum = checksum((unsigned short*)szSendBuf, sizeof(PSD_TCP_HEADER) + sizeof(TCP_HEADER));
 
        memcpy(szSendBuf, &IP_HEADER, sizeof(IP_HEADER));
        memcpy(szSendBuf + sizeof(IP_HEADER), &TCP_HEADER, sizeof(TCP_HEADER));
        memset(szSendBuf + sizeof(IP_HEADER) + sizeof(TCP_HEADER), 0, 4);
        IP_HEADER.checksum = checksum((unsigned short*)szSendBuf, sizeof(IP_HEADER) + sizeof(TCP_HEADER));
 
        memcpy(szSendBuf, &IP_HEADER, sizeof(IP_HEADER));
 
        int rect = sendto(sendsocket, szSendBuf, sizeof(IP_HEADER) + sizeof(TCP_HEADER), 0,
                (struct sockaddr*) &addr_in, sizeof(addr_in));
        if (rect == SOCKET_ERROR)
        {
                printf("send error!:%d\n", WSAGetLastError());
                return false;
        }
        else
                printf("send ok!\n");
 
        closesocket(sendsocket);
        WSACleanup();
 
        return rect;
}
 
 
int main()
{
        int port;
        printf("please enput port number=\n");
        scanf("%d", &port);
        attacker2target(port);
        return 0;
}

 

3.在浏览器中输入URL到显示网页是一个什么样的过程,使用了哪些协议?

https://www.cnblogs.com/kongxy/p/4615226.html

4.一台刚刚接入互联网的WEB服务器第一次被访问到时,不同协议的发生顺序是:

ARP -> DNS -> HTTP

阿里2015基础平台研发工程师实习生笔试卷:

https://blog.csdn.net/zengxiantao1994/article/details/55252979

5.ICMP报文和ping命令

https://blog.csdn.net/u012813201/article/details/70216725

5.1 禁止ping就是禁止ICMP协议吗?

应该说:禁用了ICMP协议当然就禁止了ping!

6.网页不能访问,但QQ正常。是什么情况?

https://zhidao.baidu.com/question/74644953.html

 

5.数据库

1.数据库事务四大特性(简称ACID) 

1、原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。

2、一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。

3、隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。

4、持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。

 

2.数据库的索引

参考:https://www.cnblogs.com/aspwebchh/p/6652855.html

「数据库」和「数据库索引」这两个东西是在服务器端开发领域应用最为广泛的两个概念,熟练使用数据库和数据库索引是开发人员在行业内生存的必备技能

索引(index)是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息。

索引的一个主要目的就是加快检索表中数据的方法,亦即能协助信息搜索者尽快的找到符合限制条件的记录ID的辅助数据结构。

  • 为什么要给表加上主键?

  • 为什么加索引后会使查询变快?

  • 为什么加索引后会使写入、修改、删除变慢?

  • 什么情况下要同时在两个字段上建索引?

想要理解索引原理必须清楚一种数据结构「平衡树」(非二叉),也就是b tree或者 b+ tree,重要的事情说三遍:“平衡树,平衡树,平衡树”。当然, 有的数据库也使用哈希桶作用索引的数据结构 , 然而, 主流的RDBMS都是把平衡树当做数据表默认的索引数据结构的。​​​​​​​

然而, 事物都是有两面的, 索引能让数据库查询数据的速度上升, 而使写入数据的速度下降,原因很简单的, 因为平衡树这个结构必须一直维持在一个正确的状态, 增删改数据都会改变平衡树各节点中的索引数据内容,破坏树结构, 因此,在每次数据改变时, DBMS必须去重新梳理树(索引)的结构以确保它的正确,这会带来不小的性能开销,也就是为什么索引会给查询以外的操作带来副作用的原因。

非聚集索引和聚集索引的区别在于, 通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据,如下图

不管以任何方式查询表, 最终都会利用主键通过聚集索引来定位到数据, 聚集索引(主键)是通往真实数据所在的唯一路径。

然而, 有一种例外可以不使用聚集索引就能查询出所需要的数据, 这种非主流的方法 称之为「覆盖索引」查询, 也就是平时所说的复合索引或者多字段索引查询。 文章上面的内容已经指出, 当为字段建立索引以后, 字段中的内容会被同步到索引之中, 如果为一个索引指定两个字段, 那么这个两个字段的内容都会被同步至索引之中。

先看下面这个SQL语句

//建立索引

create index index_birthday on user_info(birthday);

//查询生日在1991年11月1日出生用户的用户名

select user_name from user_info where birthday = '1991-11-1'

这句SQL语句的执行过程如下

首先,通过非聚集索引index_birthday查找birthday等于1991-11-1的所有记录的主键ID值

然后,通过得到的主键ID值执行聚集索引查找,找到主键ID值对就的真实数据(数据行)存储的位置

最后, 从得到的真实数据中取得user_name字段的值返回, 也就是取得最终的结果

我们把birthday字段上的索引改成双字段的覆盖索引

create index index_birthday_and_user_name on user_info(birthday, user_name);

这句SQL语句的执行过程就会变为

通过非聚集索引index_birthday_and_user_name查找birthday等于1991-11-1的叶节点的内容,然而, 叶节点中除了有user_name表主键ID的值以外, user_name字段的值也在里面, 因此不需要通过主键ID值的查找数据行的真实所在, 直接取得叶节点中user_name的值返回即可。 通过这种覆盖索引直接查找的方式, 可以省略不使用覆盖索引查找的后面两个步骤, 大大的提高了查询性能,如下图

 数据库索引的大致工作原理就是像文中所述, 然而细节方面可能会略有偏差,这但并不会对概念阐述的结果产生影响 。

数据库书籍推荐:

《SQL Server2005技术内幕之T-SQL查询》

《关系数据库系统概论》第四版

《数据库系统概念》

参考:https://www.cnblogs.com/aspwebchh/p/6652855.html

 

 

6.计算机系统(硬件、汇编)(计算机组成原理、微机原理与接口)

 

 

软件开发面试资料整理

计算机基础知识

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值