C++基础

  1. C 和 C++ 区别
    面向对象与面向过程的区别
    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
    面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
    例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
    而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
    可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。面向过程的区别

  2. C++中有了malloc / free , 为什么还需要 new / delete
    1,malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
    2,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
    3,因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

  3. 编写类String 的构造函数,析构函数,拷贝构造函数和赋值函数
    class String
    {
    public:
    String(const char *str = NULL); // 普通构造函数
    String(const String &other); // 拷贝构造函数
    ~String(void); // 析构函数
    String & operator = (const String &other); // 赋值函数
    private:
    char *m_data; // 用于保存字符串
    };
    构造函数
    1、构造函数在构造对象时使用;
    2、传入参数的判断;
    3、对象的初始化问题。
    String::String(const char *str)
    {
    if ( NULL == str)
    {
    m_data = new char[1]
    *m_data = ‘\0’;
    }
    else
    {
    拷贝构造函数
    1、拷贝构造函数必须在构造对象时使用,即定义对象时;
    2、对象初始化问题。
    String::String(const String &other)
    {
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data,other.m_data);
    }
    赋值函数
    1、 赋值函数使用时,对象肯定已经建立;
    2、 赋值前,判断是否是自我赋值;
    3、 赋值前,内存空间的准备:
    由于赋值前,对象已占有一定大小内存,但是赋值对象所占内存大小与对象已占的内存大小不一定一致;先释放对象已占的内存,然后分配心内存。
    4、正常赋值
    String & String::operator = (const String &other)
    {
    if (&other == this)
    {
    return *this;
    }

    delete [] m_data;
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data,other.m_data);

    return *this;
    }
    析构函数
    资源的释放
    String::~String(void)
    {
    delete []m_data;
    }
    (1)普通构造函数:这里判断了传入的参数是否为NULL。如果是NULL,初始化一个字节的空字符串(包括结束符’\0’);如果不是,分配足够大小长度的堆内存保存字符串。
    (2)拷贝构造函数:只是分配足够小长度的堆内存保存字符串。
    (3)析构函数:如果类私有成员m_String不为NULL,释放m_String指向的堆内存,并且为了避免产生野指针,将m_String赋为NULL。
    (4)赋值函数:首先判断当前对象与引用传递对象是否是同一个对象,如果是,不做操作直接返回;否则先释放当前对象的堆内存,然后分配足够大小长度的堆内存拷贝字符串。
    1、 拷贝构造函数与赋值函数的区别?
      在看到“=”操作符为对象赋值的时候,如果是在对象定义时(Test B = (Test)c),此时调用拷贝构造函数;如果不是在对象定义赋值时(B = c),此时调用赋值函数。
      注:构造函数、拷贝构造函数,带有构造两个字,顾名思义,就是在对象声明或定义时才会使用。
    2、拷贝构造函数与赋值函数定义的区别?
      内存空间角度:
      1)拷贝构造函数的使用,是在建立对象时;当时对象没有占有内存,故不需要释放内存,不重新建立内存空间。
      2)赋值函数的使用,是在对象建立后;当时对象已经占有内存,故需要释放先前内存,然后重新获取内存空间。

  4. 多态的实现
    多态的概念:让一个对象能够表现出多种的状态(类型)。
    用一句话来描述多态:通过继承实现的不同对象调用相同的方法,表现出不同的行为,称之为多态。
    多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。让程序具有扩展性,节省成本,提高效率。
    实现多态的3种方式: 虚方法、抽象类、接口。
    虚方法
    将父类的方法标记为虚方法,使用关键字virtual,这个方法可以被子类重新写一遍。
    在父类的方法前面加上一个virtual,在子类的方法前面加上一个override;如果子类的方法前面不加override,编译器不会报错,但这样的话,就无法通过父类来调用子类的方法,因为这个方法成了子类的独有的方法,只是名字与父类相同而已,与父类无关。
    注意:如果需要对父类的方法进行重写的话,一定要在子类的方法面前加上overrie进行修饰。
    什么时候使用:几个类可以提取出一个共同的父类,它们都要完成相同的事情,而且这个父类可以被实例化。
    例如: 员工9点打卡,经理10点打卡,董事长11点打卡; 这几个类共同的父类是员工类,它们都要完成打卡这件事情。
    public class Employee
    {
    public virtual void DaKa()
    {
    Console.WriteLine(“九点打卡”);
    }
    }

    public class Manager : Employee
    {
    public override void DaKa()
    {
    Console.WriteLine(“经理10点打卡”);
    }
    }

    public class Chairman : Employee
    {
    public override void DaKa()
    {
    Console.WriteLine(“董事长11点打卡”);
    }
    }
    Employee emp = new Employee();
    Manager mg = new Manager();
    Chariman cm = new Chariman();
    Employee[] emps = { emp, mg,cm };
    for (int i = 0; i < emps.Length; i++)
    {
    emps[i].DaKa();
    }
    Console.ReadKey();
    使用虚方法需要注意的几点:
      1)父类中如果有方法需要让子类重写,则可以将该方法标记为virtual
      2)虚方法在父类中必须有实现,哪怕是空实现
      3)虚方法子类可以重写(override),也可以不重写

  5. 不调用C/C++ 的字符串库函数,编写strcpy
    char * strcpy(char * strDest,const char * strSrc)
    {
    if ((strDest==NULL)||strSrc==NULL))
    return NULL;
    char * strDestCopy=strDest;
    while ((*strDest++=*strSrc++)!=’\0’);
    *strDest = ‘\0’;
    return strDestCopy;
    }

  6. 关键字static的作用

    1. 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
    2. 在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问
    3. 在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内
    4. 在类的static 成员变量属于整个类所拥有,对类的所以对象只有一份拷贝
    5. 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量

      介绍它最重要的一条:隐藏。(static函数,static变量均可) –> 对应上面的2、3项
      当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。
      举例来说明。同时编译两个源文件,一个是a.c,另一个是main.c。
      //a.c
      char a = ‘A’; // global variable
      void msg()
      {
      printf(“Hello\n”);
      }

    //main.c
    int main()
    {
    extern char a; // extern variable must be declared before use
    printf(“%c “, a);
    (void)msg();
    return 0;
    }
    程序的运行结果是:
    A Hello
    为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?
    前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,
    因此对于另外的源文件main.c是可见的。
    如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。
    利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏

  7. 在c++程序中调用被C编译器编译后的函数,为什么要加extern“C”
    C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同,
    假设某个函数原型为:

  8. void foo(int x, inty);
    该函数被C编译器编译后在库中的名字为: _foo
    而C++编译器则会产生像: _foo_int_int 之类的名字。
    为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern “C”。

  9. 头文件种的ifndef/define/endif 是干什么用的
    防止头文件被重复包含

  10. 线程和进程的联系和区别
    1、 线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。
    2、 一个没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个进程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成的。
    3、 系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。那就是说,出了CPU之外(线程在运行的时候要占用CPU资源),计算机内部的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。
    4、 与进程的控制表PCB相似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。
    5、 进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。

  11. 线程有哪几种状态
    线程,有时称为轻量级进程,是CPU使用的基本单元;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)。
    线程有四种状态:新生状态、可运行状态、被阻塞状态、死亡状态。状态之间的转换如下图所示:

  12. 进程间的通信方式
    管道、有名管道、信号、共享内存、消息队列、信号量、套接字、文件.

  13. 线程同步和线程互斥的区别
    线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
    线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。

  14. 线程同步的方式
    Linux: 互斥锁、条件变量和信号量
    通过锁机制实现线程间的同步。

  15. 初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
    静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
  16. 加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  17. 解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
  18. 销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
    int pthread_mutex_destroy(pthread_mutex *mutex);
    与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。
    条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
  19. 初始化条件变量。
    静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  20. 等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
  21. 激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
  22. 清除条件变量。无线程等待,否则返回EBUSY
    int pthread_cond_destroy(pthread_cond_t *cond);
    如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以”sem_”打头。线程使用的基本信号量函数有四个。
  23. 信号量初始化。
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
  24. 等待信号量。给信号量减1,然后等待直到信号量的值大于0。
    int sem_wait(sem_t *sem);
  25. 释放信号量。信号量值加1。并通知其他等待线程。
    int sem_post(sem_t *sem);
  26. 销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
    int sem_destroy(sem_t *sem);

13.网络七层

  1. TCP和UDP有什么区别
    TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。
    当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
    TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
    UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。
    UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
    由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

  2. 编写socket套接字的步骤
    套接字(socket)概念
    套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
    应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
    建立socket连接
    建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
    套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
    服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
    客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
    连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

    服务器端程序的编写步骤:
    第一步:调用socket()函数创建一个用于通信的套接字。
    第二步:给已经创建的套接字绑定一个端口号,这一般通过设置网络套接口地址和调用bind()函数来实现。
    第三步:调用listen()函数使套接字成为一个监听套接字。
    第四步:调用accept()函数来接受客户端的连接,这是就可以和客户端通信了。
    第五步:处理客户端的连接请求。
    第六步:终止连接。
    客户端程序编写步骤:、
    第一步:调用socket()函数创建一个用于通信的套接字。
    第二步:通过设置套接字地址结构,说明客户端与之通信的服务器的IP地址和端口号。
    第三步:调用connect()函数来建立与服务器的连接。
    第四步:调用读写函数发送或者接收数据。
    第五步:终止连接。

  3. TCP三次握手和四次挥手, 以及各个状态的作用
    https协议是在http协议之上封装了SSL协议,为了完成客户端和服务端的双向认证和可靠传输,需要在通信之前双方进行多次的握手协商。TCP协议与https协议类似。
    TCP的连接(俗称三次握手)

在TCP协议数据报文的头部(TCP Header)结构中有32位序号(Sequence number) 和32位确认序号(Acknowledge number):SYN,ACK。
  第一次握手 Client发送位码为syn=1,随机产生seq number的数据包到server,server由SYN=1知道,Client要求连接;
  第二次握手 server收到请求后要确认连接信息,向Client发送ack number=(Client的seq+1),syn=1,ack=1,随机产生seq number的包
第三次握手 Client收到后检查ack number是否正确,即第一次发送的seq number+1,以及ack是否为1,若正确,Client会再发送ack number=(server的seq+1),ack=1,server收到后确认seq值与ack=1则连接建立成功。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
TCP的关闭(俗称四次挥手)
TCP连接是全双工的,可以同时发送和接受数据,关闭的时候要关闭这两个方向的通道
下图是 Client主动关闭连接

第一次挥手:Client给Server发送FIN,请求关闭连接
第二次挥手:Server收到FIN之后给Client返回确认ACK,同时关闭Receive通道,Client收到对自己的FIN确认后,关闭Send通道
第三次挥手: Server关闭连接,给Client发送FIN
第四次挥手:Client收到后给Server回复ACK确认,同时Client关闭Receive通道,进入TIME_WAIT状态。Server接收到Client对自己的FIN的确认ACK,关闭Send通道
状态的说明
CLOSE_WAIT: 表示在等待关闭。当主动关闭连接的一方关闭SOCKET后发送FIN报文给被动关闭一方,被动关闭一方回应一个ACK报文给对方,此时被动关闭一方则进入到CLOSE_WAIT状态
FIN_WAIT_1:是当Socket在已经连接的状态时主动关闭连接,向对方发送了FIN报文,此时该Socket进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态
FIN_WAIT_2:表示半连接,挥了两次手的状态等待对方的Fin报文
TIME_WAIT:TCP协议中主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,在TIME_WAIT期间仍然不能再次监听同样的server端口。
LAST_ACK: 被动关闭一方在发送FIN报 文后,最后等待对方的ACK报文。当收到ACK报文后进入CLOSED状态。
CLOSED:已经完全关闭.

  1. HTTP协议
    http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,
    HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。
    HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
    1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
    2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
http是要基于TCP连接基础上的,简单的说,TCP就是单纯建立连接,不涉及任何我们需要请求的实际数据,简单的传输。http是用来收发数据,即实 际应用上来的。
SOCKET连接与TCP连接
创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
Socket连接与HTTP连接
由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。
TCP和http的区别:
TCP是底层通讯协议,定义的是数据传输和连接方式的规范
HTTP是应用层协议,定义的是传输数据的内容的规范

HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP也就一定支持TCP
HTTP支持的是www服务
而TCP/IP是协议
它是Internet国际互联网络的基础。TCP/IP是网络中使用的基本的通信协议。

TCP/IP实际上是一组协议,它包括上百个各种功能的协议,如:远程登录、文件传输和电子邮件等,而TCP协议和IP协议是保证数据完整传输的两个基本的重要协议。通常说TCP/IP是Internet协议族,而不单单是TCP和IP。

  1. 静态链表和动态链表的区别
    静态链表和动态链表是线性表链式存储结构的两种不同的表示方式。
    1、静态链表是用类似于数组方法实现的,是顺序的存储结构,在物理地址上是连续的,而且需要预先分配地址空间大小。所以静态链表的初始长度一般是固定的,在做插入和删除操作时不需要移动元素,仅需修改指针。
    2、动态链表是用内存申请函数(malloc/new)动态申请内存的,所以在链表的长度上没有限制。动态链表因为是动态申请内存的,所以每个节点的物理地址不连续,要通过指针来顺序访问。

  2. 什么时候要用虚析构函数
    通过基类的指针来删除派生类的对象时,基类的析构函数应该是虚的。否则其删除效果将无法实现。
    一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,从而千万内存泄漏。
    原因:
    在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。如果想要用基类对非继承成员进行操作,则要把基类的这个操作(函数)定义为虚函数。 那么,析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。
    注意:
    如果不需要基类对派生类及对象进行操作,则不能定义虚函数(包括虚析构函数),因为这样会增加内存开销。

  3. c++怎样让返回对象的函数不调用拷贝构造函数
    拷贝构造函数前加 “explicit” 关键字

《必须掌握的20道技术面试题》
21. 请用简单的语言告诉我C++ 是什么?
答:C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛。C++支持多种编程范式 --面向对象编程、泛型编程和过程化编程。 其编程领域众广,常用于系统开发,引擎开发等应用领域,是最受广大程序员受用的最强大编程语言之一,支持类:类、封装、重载等特性!
22. C和C++的区别?
答:
C++在C的基础上增添类
C是一个结构化语言,它的重点在于算法和数据结构。
C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

  1. 什么是面向对象(OOP)?
    答:面向对象是一种对现实世界理解和抽象的方法、思想,通过将需求要素转化为对象进行问题处理的一种思想。
  2. 设计模式懂嘛,简单举个例子?
    答:设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
    比如单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
    适用于:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
    比如工厂模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
    适用于:当一个类不知道它所必须创建的对象的类的时候;当一个类希望由它的子类来指定它所创建的对象的时候;当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
    25. STL库用过吗?常见的STL容器有哪些?算法用过哪几个?
    答:STL包括两部分内容:容器和算法。(重要的还有融合这二者的迭代器)
    容器,即存放数据的地方。比如array等。
    在STL中,容器分为两类:序列式容器和关联式容器。
    序列式容器,其中的元素不一定有序,但都可以被排序。如:vector、list、deque、stack、queue、heap、priority_queue、slist;
    关联式容器,内部结构基本上是一颗平衡二叉树。所谓关联,指每个元素都有一个键值和一个实值,元素按照一定的规则存放。如:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap。
    下面各选取一个作为说明。
    vector:它是一个动态分配存储空间的容器。区别于c++中的array,array分配的空间是静态的,分配之后不能被改变,而vector会自动重分配(扩展)空间。
    set:其内部元素会根据元素的键值自动被排序。区别于map,它的键值就是实值,而map可以同时拥有不同的键值和实值。
    算法,如排序,复制……以及个容器特定的算法。这点不用过多介绍,主要看下面迭代器的内容。
    迭代器是STL的精髓,我们这样描述它:迭代器提供了一种方法,使它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构。它将容器和算法分开,好让这二者独立设计。
  3. const知道吗?解释其作用。
    答:
    1.const 修饰类的成员变量,表示成员常量,不能被修改。
    2.const修饰函数承诺在本函数内部不会修改类内的数据成员,不会调用其它非 const 成员函数。
    3.如果 const 构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数。
    4.const 函数只能调用 const 函数。非 const 函数可以调用 const 函数。
    5.类体外定义的 const 成员函数,在定义和声明处都需要 const 修饰符。。
  4. 堆和栈的区别?堆和栈的生命周期?
    答:
    一、堆栈空间分配区别:
    1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
    2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
    二、堆栈缓存方式区别:
    1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
    2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
    三、堆栈数据结构区别:
    堆(数据结构):堆可以被看成是一棵树,如:堆排序;
    栈(数据结构):一种先进后出的数据结构。

  5. 解释下封装、继承和多态?
    答:
    一、封装:
    封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。
    封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
    二、继承:
    继承主要实现重用代码,节省开发时间。
    子类可以继承父类的一些东西。
    三、多态
    多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

  6. 指针和引用的区别?
    答:
  7. 指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用仅是个别名;
  8. 引用使用时无需解引用(*),指针需要解引用;
  9. 引用只能在定义时被初始化一次,之后不可变;指针可变;
  10. 引用没有 const,指针有 const;
  11. 引用不能为空,指针可以为空;
  12. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
  13. 指针和引用的自增(++)运算意义不一样;
  14. 指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
    9.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
  15. 什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?
    答:用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。
    使用的时候要记得指针的长度。
    malloc的时候得确定在那里free.
    对指针赋值的时候应该注意被赋值指针需要不需要释放.
    动态分配内存的指针最好不要再次赋值.
  16. new和malloc的区别?
    答:
    1、malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
    2、对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
    3、由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
    4、C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
    5、new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。
  17. TCP和UDP通信的差别?什么是IOCP?
    答:
    1.TCP面向连接, UDP面向无连接的
    2.TCP有保障的,UDP传输无保障的
    3.TCP是效率低的,UDP效率高的
    4.TCP是基于流的,UDP基于数据报文
    5.TCP传输重要数据,UDP传输不重要的数据
    IOCP全称I/O Completion Port,中文译为I/O完成端口。
    IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序。
    与使用select()或是其它异步方法不同的是,一个套接字[socket]与一个完成端口关联了起来,然后就可继续进行正常的Winsock操作了。然而,当一个事件发生的时候,此完成端口就将被操作系统加入一个队列中。然后应用程序可以对核心层进行查询以得到此完成端口。
  18. 同步IO和异步IO的区别?
    答:
    A. 同步
    所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
    按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。
    但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
    最常见的例子就是 SendMessage。
    该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
    当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。
    B. 异步
    异步的概念和同步相对。
    当一个异步过程调用发出后,调用者不会立刻得到结果。
    实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。
  19. 解释C++中静态函数和静态变量?
    答:
    (1)类静态数据成员在编译时创建并初始化:在该类的任何对象建立之前就存在,不属于任何对象,而非静态类成员变量则是属于对象所有的。类静态数据成员只有一个拷贝,为所有此类的对象所共享。
    (2)类静态成员函数属于整个类,不属于某个对象,由该类所有对象共享。
    1、static 成员变量实现了同类对象间信息共享。
    2、static 成员类外存储,求类大小,并不包含在内。
    3、static 成员是命名空间属于类的全局变量,存储在 data 区的rw段。
    4、static 成员只能类外初始化。
    5、可以通过类名访问(无对象生成时亦可),也可以通过对象访问。
    1、静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。
    2、静态成员函数只能访问静态数据成员。原因:非静态成员函数,在调用时 this指针时被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。
  20. 说下你对内存的了解?
    答:
    1.栈 - 由编译器自动分配释放
    2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
    3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
    4.另外还有一个专门放常量的地方。- 程序结束释放
    5 程序代码区,存放2进制代码。
    在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的”adgfdf”这样的字符串存放在常量区。

操作系统常见面试题及答案
1. 什么是进程(Process)和线程(Thread)?有何区别?
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。 进程与应用程序的区别在于应用程序作为一个静态文件存储在计算机系统的硬盘等存储空间中,而进程则是处于动态条件下由操作系统维护的系统资源管理实体。

2. Windows下的内存是如何管理的?
Windows提供了3种方法来进行内存管理:虚拟内存,最适合用来管理大型对象或者结构数组;内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行多个进程之间共享数据;内存堆栈,最适合用来管理大量的小对象。
Window操纵内存可以分两个层面:物理内存和虚拟内存。
其中物理内存由系统管理,不允许应用程序直接访问,应用程序可见的只有一个2G地址空间,而内存分配是通过堆进行的,对于每个进程都有自己的默认堆,当一个堆创建后,就通过虚拟内存操作保留了相应大小的地址块(不占有实际的内存,系统消耗很小),当在堆上分配一块内存时,系统在堆的地址表里找到一个空闲块(如果找不到,且堆创建属性是可扩充的,则扩充堆大小)为这个空闲块所包含的所有内存页提交物理对象(物理内存上或硬盘上的交换文件上)。这时可以就访问这部分地址了。提交时,系统将对所有进程的内存统一调配,如果物理内存不够,系统试图把一部分进程暂时不访问的页放入交换文件,以腾出部分物理内存。释放内存时,只在堆中将所在的页解除提交(相应的物理对象被解除),继续保留地址空间。
如果要知道某个地址是否被占用/可不可以访问,只要查询此地址的虚拟内存状VirtualQuery),如果是提交,则可以访问。如果仅仅保留,或没保留,则产生一个软件异常。此外有些内存页可以设置各种属性。如果是只读,向内写也会产生软件异常。

3. Windows消息调度机制是?
A. 指令队列;B.指令堆栈;C.消息队列;D.消息堆栈
答案:C
处理消息队列的顺序。首先windows绝对不是按队列先进先出的次序来处理的,而是有一定优先级的。优先级通过消息队列的状态标志来实现的。首先最高优先级的是别的线程发过来的消息(通过sendmessage),其次是处理登记消息队列消息,再次处理QS_QUIT标志,再处理虚拟输入队列,再处理wm_paint最后是wm_timer

4. 描述实时系统的基本特性
在特定时间内完成特定的任务,实时性与可靠性。
所谓“实时操作系统”,实际上是指操作系统工作时,其各种资源可以根据需要随时进行动态分配。由于各种资源可以进行动态分配,因此其处理事务的能力较强、速度较快。

5. 中断和轮询的特点。
对I/O设备的程序轮询的方式,是早期的计算机系统对I/O设备的一种管理方式。它定时对各种设备轮流询问一遍有无处理要求。轮流询问之后,有要求的,则加以处理。在处理I/O设备的要求之后,处理机返回继续工作。尽管轮询需要时间,但轮询要比I/O设备的速
度要快得多,所以一般不会发生不能及时处理的问题。当然,再快的处理机,能处理的输入输出设备的数量也是有一定限度的。而且,程序轮询毕竟占据了CPU相当一部分处理时间,因此程序轮询是一种效率较低的方式,在现代计算机系统中已很少应用。
程序中断通常简称中断,是指CPU在正常运行程序的过程中,由于预选安排或发生了各种随机的内部或外部事件,使CPU中断正在运行的程序,而转到为响应的服务程序去处理。
轮询——效率低,等待时间很长,CPU利用率不高
中断——容易遗漏一些问题,CPU利用率高

6. 什么是临界区?如何解决冲突?
每个进程中访问临界资源的那段程序称为临界区,每次只准许一个进程进入临界区,进入后不允许其他进程进入。
① 如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
② 任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。
③ 进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
④ 如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。

7. 说说分段和分页
页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率;或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。
段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好的满足用户的需要。页的大小固定且由系统确定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而一个系统只能有一种大小的页面。 段的长度却不固定,决定于用户所编写的程序,通常由编辑程序在对源程序进行编辑时,根据信息的性质来划分。
分页的作业地址空间是维一的,即单一的线性空间,程序员只须利用一个记忆符,即可表示一地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址

8. 进程通信有哪些方式?
管道通信、消息通信、内存共享

9. 说出你所知道的保持进程同步的方法?
进程间同步的主要方法有内存屏障,互斥锁,信号量和锁,管程,消息,管道。

10. 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。
11. Linux文件属性有哪些?(共十位)
-rw-r–r-l
那个是权限符号,总共是- — — —这几个位
第一个短横处是文件类型识别符:-表示普通文件;c表示字符设备(character);b表示块
设备(block);d表示目录(directory);l表示链接文件(link)
后面第一个三个连续的短横是用户权限位(User),第二个三个连续短横是组权限位(Group),第三个三个连续短横是其他权限位(Other)。每个权限位有三个权限,r(读权限),w(写权限),x(执行权限)。如果每个权限位都有权限存在,那么满权限的情况就是:-rwxrwxrwx;权限为空的情况就是- — — —。
权限的设定可以用chmod命令,其格式位:chomod ugo+/-/=rwx filename/directory。例如:
一个文件aaa具有完全空的权限- — — —。以下命令:
chmod u+rw aaa (给用户权限位设置读写权限,其权限表示为:- rw- — —)
chmod g+r aaa (给组设置权限为可读,其权限表示为:- — r– —)
chmod ugo+rw aaa (给用户,组,其它用户或组设置权限为读写,权限表示为:- rw- rw- rw-)
如果 aaa 具有满权限 - rwx rwx rwx,以下命令为:
chmod u-x aaa (去掉用户可执行权限,权限表示为:- rw- rwx rwx)
如果要给aaa赋予制定权限- rwx r-x r-x,命令为:
chmod u=rwx,go=rx aaa

12. UNIX显示文件夹中文件名的命令是什么?能使文件内容显示在屏幕的命令是什么?
ls cat
type tail

13. makefile文件的作用是什么?
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

14. 简术ISO OSI的物理层Layer1,链路层Layer2,网络层Layer3的任务
网络层:资料传送的目的地寻址,再选择出传送资料的最佳路线;
链路层:负责网络上资料封包如何传送的方式;
物理层:在设备与传输媒介之间建立及终止连接。参与通讯过程使得资源可以在共享的多用户中有效分配,对信号进行调制或转换使得用户设备中的数字信号定义能与信道上实际传送的数字信号相匹配。

15. CPU在上电后,进入操作系统的main()之前必须做什么?
加电后,会触发CPU的reset信号,导致CPU复位,然后CPU会跳到(arm下0x00000000,x86下0xfffffff0)执行指令。主要是做CPU初始化,确定CPU的工作模式,mmu初始化。建立页表段表,初始化中孤单控制器和中断向量表,初始化输入和输出,初始化nandflash,把OS的TEXT区加载到sdram,然后跳转到sdram的main()

16. 什么是中断?中断时CPU做什么工作?
中断是指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序。待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。

17. 存储过程是什么?有什么用?有什么优点?
存储过程(Stored Procedure)是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是SQL 语句和可选控制流语句的预编译集合,以一个名称存储并作为一个单元处理。存储过程存储在数据库内,可由应用程序通过一个调用执行,而且允许用户声明变量、有条件执行以及其它强大的编程功能。存储过程在创建时即在服务器上进行编译,所以执行起来比单个SQL语句快。
存储过程的优点:(1)存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度;(2)当对数据库进行复杂操作时(如对多个表进行Update, Insert, Query, Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用;(3)存储过程可以重复使用,可减少数据库开发人员的工作量;(4)安全性高,可设定只有某此用户才具有对指定存储过程的使用权。
存储过程的缺点:(1)如果更改范围大到需要对输入存储过程的参数进行更改,或者要更改由其返回的数据,则您仍需要更新程序集中的代码以添加参数、更新 GetValue() 调用,等等,这时候估计比较繁琐了。(2)可移植性差。由于存储过程将应用程序绑定到 SQL Server,因此使用存储过程封装业务逻辑将限制应用程序的可移植性。

18. 你知道操作系统的内容分为几块吗?什么叫做虚拟内存?他和主存的关系如何?内存管理属于操作系统的内容吗?
操作系统的主要组成部分:进程和线程的管理,存储管理,设备管理,文件管理。
虚拟内存是一些系统页文件,存放在磁盘上,每个系统页文件大小也为4K,物理内存也被分页,每个页大小也为4K,这样虚拟页文件和物理内存页就可以对应,实际上虚拟内存就是用于物理内存的临时存放的磁盘空间。页文件就是内存页,物理内存中每页叫物理页,磁盘上的页文件叫虚拟页,物理页+虚拟页就是系统所以使用的页文件的总和。属于。

20. OS中如何实现物理地址到逻辑地址的转换?
CPU要利用其段式内存管理单元,先将逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。

22. 线程是否具有相同的堆栈?dll是否有独立的堆栈?
每个线程有自己的堆栈。
DLL中有没有独立的堆栈,这个问题不好回答,或者说这个问题本身是否有问题。因为DLL中的代码是被某些线程所执行,只有线程拥有堆栈,如果DLL中的代码是EXE中的线程所调用,那么这个时候是不是说这个DLL没有自己独立的堆栈?如果DLL中的代码是由DLL自己创建的线程所执行,那么是不是说DLL有独立的堆栈?
以上讲的是堆栈,如果对于堆来说,每个DLL有自己的堆,所以如果是从DLL中动态分配的内存,最好是从DLL中删除,如果你从DLL中分配内存,然后在EXE中,或者另外一个DLL中删除,很有可能导致程序崩溃。

23. 网络编程中设计并发服务器,使用“多进程”与“多线程”,请问有什么区别?
进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。
线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
两者都可以提高程序的并发度,提高程序运行效率和响应时间。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

24. 解释一下分页式管理.
用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。

25. 解释一下P操作与V操作
P就是请求资源,V就是释放资源

26. 什么是缓冲区溢出?有什么危害?其原因是什么?
缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢的数据覆盖在合法数据上,
危害:在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。
造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数

27. 什么是死锁?其条件是什么?怎样避免死锁?
死锁的概念:在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它们现在保持着的资源,否则就不能向前推进。此时,每个进程都占用了一定的资源但是又不能向前推进,称这一组进程产生了死锁。 通俗的讲,就是两个或多个进程无止境的等候着永远不会成立的条件的一种系统状态。
死锁产生的原因主要是:(1)系统资源不足;(2)进程运行推进的顺序不合适;(3)资源分配不当。
产生死锁的必要条件:
(1)互斥(mutual exclusion),一个资源每次只能被一个进程使用;
(2)占有且等待(hold and wait),一个进程因请求资源而阻塞时,对已获得的资源保持不放;
(3)不可抢占(no preemption),进程已获得的资源,在未使用完之前,不能强行剥夺;
(4)环形等待(circular wait),若干进程之间形成一种首尾相接的循环等待资源关系。这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的解除与预防:理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和 解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外, 也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
死锁的处理策略:鸵鸟策略、预防策略、避免策略、检测与解除死锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值