浅析C++基础知识

   近期想对C++的面试题目进行一下更加详细的整理。事实上认真思考一下C++程序猿的面试,我们能够发现对程序猿的能力的考察总是万变不离当中,这些基础知识主要分为五部分:一、 C/C++基础知识 二、 C/C++的一些特性,如面向对象,内存管理  三、 基础的数据结构编程的知识。 四、stl的一些基础知识。五、网络编程、多线程的知识、异常处理基础知识

    本文试图覆盖C/C++面试的每一个知识点,所以对每一个知识点介绍的并不深入。本文适合已经对一下详细知识有所了解的人,我对每一个点都有粗略的解说,假设想深入理解请阅读相关详细知识。

    一、 C/C++的基础知识:包含指针、字符串处理、内存管理。

    二、面向对象基础知识:包含多态、继承、封装。 多态的实现方式?多态的优点?

    三、基础的数据结构面试:数组、链表、字符串操作、二叉树。这里要说的话就说的非常多了,详细的有使用一些数据结构包含stl list, vector, map, hashmap, set。

    四、stl的一些基础知识。

    五、网络编程、多线程和异常处理的基础知识。


一、C/C++基础知识:

             1. C/C++内存管理:C/C++的内存分为:堆、栈、自由数据区、静态数据区和常量数据区。

                     栈: 函数运行时,函数内部的局部变量在栈上创建,函数运行结束栈的存储空间被自己主动释放。

                     堆:就是那些new分配的内存块,须要程序猿调用delete释放掉。

                     自由数据区:就是那些调用malloc的内存块,须要程序猿调用free释放掉。

                     全局存储区:全局变量和静态变量被存放到同一块存储区域。

                     静态存储区:这里存放常量,不同意改动。

                     堆和栈的差别:a. 管理方式不同:堆须要程序猿手动维护。 b. 栈的存储空间远小于堆。堆差点儿不受限制。 c. 生长方式不同。栈是从高地址空间向低地址空间生长,堆是从低地址空间向高地址空间生长。 d. 效率不同:堆要远快于栈。

                     new和delete的差别:new和delete是C++的函数会调用,构造函数和析构函数。而malloc和delete仅仅是分配相应的存储空间。

                     注意问题: 1.  意外处理:内存申请失败处理。2. 内存泄漏:申请的地址空间一定要释放。 3. 野指针:避免指针未赋值使用,一定要初始化。

                     给出一个图吧,关于内存空间的分配的:

 

左边的是UNIX/LINUX系统的运行文件,右边是相应进程逻辑地址空间的划分情况。     

          2.  小端存储和大端存储:

                     小段:高位存在高地址上;大端:低位存在高地址上。

                     判定代码:

                                       unsigned short test = 0x1234;

                                 if( *(unsigned char *)&test=0x12 )//

                                        return Big_Edian;//高位在低地址空间 大端存储

          3.  C++的指针使用:    

                     a. 指针数组 与 数组指针: 注意[]的结合优先级比*要高。那么int *p[3];我们就得到了一个数组,数组的每一个元素是int *的。而int (*p)[3];//我们就得到了一个指针,指向的是一个长度为3的一维数组。

                     b. 函数指针:比如对函数:int funcA(int a,int b);  我们能够定义一个指针指向这个函数:int (*func)(int,int);  func=funcA;

                     c. ptr++; 指针ptr的值加上了sizeof(ptr); 

           4. 运算符优先级:

                     a. 比較常考的就是 ++和--与*、&、.的优先级对照:注意正确的优先级是:.  >  ++  >  --  >  *  >  &

           5. 二维数组的动态申请:

                     int **a=new int *[10];

                     for(i=0;i<10;i++)

                           a[i]=new int[10];

           6.  extern "C"声明的作用:

                     C++中引用C语言的函数和变量须要使用extern "C"来进行修饰。由于C++为了支持多态,函数和变量命名的方式和C不同,因此假设一个模块的函数和变量须要被其它模块使用,就须要使用extern "C"来修饰。该修饰制定编译器变量和函数是依照C语言方式进行链接的。

          7. 指针和引用的差别,指针和数组的差别,const与define的差别:

                     指针和引用的差别:引用能够看作是变量的别名,不能够指向其它的地址,不能为空。

                     指针和数组的差别:指针能够指向其它地址,使用sizeof计算时两者的结果不同。

                     const与指针的差别:const定义的变量有类型,而define仅仅是编译器的简单替换。

           8. 怎样判定程序是由C/C++编译器编译出来的?

                     #ifdef __cplusplus

                     #else

                     #endif

           9. const的作用,static的作用。

                     a. const 指定变量不可改动。

                     b. static 指定变量存在静态数据区,而且仅仅初始化一次。

           10. 变量字节对齐:为了加快内存寻址的效率,在C++内存管理时一般选取的是4字节对齐。例如以下变量占用8个字节:

                     struct Node{

                           int a,

                           char b

                     };

           11. const与指针:

                     char * const p; //指针不可改

           char const *p; //指针指向的对象不可改动

     12. 操作符重载:

           C/C++ 里大多数运算符都能够在 C++ 中被重载。C 的运算符中仅仅有 . 和 ?:(以及sizeof,技术上能够看作一个运算符)不能够被重载。C++ 添加了一些自己的运算符,除了 :: 和 .* 外,大多数都能够被重载。

           a. 单目操作符:

               Point &Point:: operator++();//++a;

               Point &Point:: operator++(int);//a++

           b. 双目操作符:

               Point &Point:: operator+=(Point b)// +=

           源码例如以下:

class Point{
public:
    int x;
    int y;
public:
    void Print(){
        cout<<"x="<<this->x<<endl;
        cout<<"y="<<this->y<<endl;
    }
    Point(){
        x=0;
        y=0;
    }
    Point(const Point &a){
        x=a.x;
        y=a.y;
    }
    Point(int x,int y){
        this->x=x;
        this->y=y;
    }
    Point& operator+=(Point b);
    Point& operator++();
    Point& operator++(int);
};
Point& Point::operator+=(Point b){
    x += b.x;
    y += b.y;
    return *this;
}

Point& Point::operator++(){
    this->x++;
    this->y++;
    return *this;
}
Point& Point::operator++(int){
    Point a(*this);
    this->x++;
    this->y++;
    return a;
}
int main(){
    Point *a=new Point(1,2);
    Point *b=new Point(3,4);
    Point c(10,20);
    *a+=c;
    a->Print();
    ++c;
    c.Print();
    c++;
    c.Print();
    return 0;
}

          13. 函数调用传值与传引用

                     传值不会改动传入參数的值,而传引用会改动传入參数的值。

          14. volatile与C/C++的四种cast函数:

                     volatile修饰的变量,编译器不会做优化,每次都是到内存中去读取或者写入改动。C++有四种cast函数:static_cast,dynamic_cast,const_cast,volatile_cast。

          15. C++ 类的实例化假设没有參数是不须要括号的,否者就是调用函数而且返回相应的对象,比如例如以下代码就会报错:

struct Test
{
	Test(int ) { }
	Test() { }
	void fun() { }
};

int main(void)
{
	Test a(1);
	a.fun();
	Test b;
	b.fun();
	return 0;
}

          15. C++ 里面的const修饰函数时,标志该函数不会改动this指针的内容。并且const不能修饰非类内部的函数。

          16. const能够重载,这是基于编译器编译时对函数进行重命名实现的。比如f( int a), f(const int a)是两个不同的函数了。

          17. override(覆盖), overload(重载), polymorphism(多态)

          18. const char* a, char const*, char*const的差别: const char * pa;//一个指向const类型的指针,char * const pc;//const修饰的指针

          19. typename和class的差别:在模版中使用时,class与typename能够替换。在嵌套依赖类型中,能够声明typename后面的字符串为类型名,而不是变量。比如:

class MyArray //临时还不是非常懂,就先睡觉了。
{ 
public:
typedef int LengthType;
.....
}

template<class T>
void MyMethod( T myarr ) 
{ 
typedef typename T::LengthType LengthType; 
LengthType length = myarr.GetLength; 
}


二、面向对象和C++特性描写叙述:

             1. 面向对象三个基本特征:继承、多态和封装。

          2. 多态:多态是面向对象继承和封装的第三个重要特征,它在执行时通过派生类和虚函数来实现,基类和派生类使用相同的函数名,完毕不同的操作和实现相隔离的还有一类接口。多态提高了代码的隐藏细节实现了代码复用。

          3. 什么是纯虚函数和纯虚类:有些情况下基类不能对虚函数有效的实现,我们能够把它声明为纯虚函数。此时基类也无法生成对象。

          4. 什么是虚函数:虚函数是类内部使用virtual修饰的,訪问权限是public的,而且非静态成员函数。虚函数是为了实现动态连接,定义了虚函数以后对基类和派生类的虚函数以相同形式定义,当程序发现virtual虚函数以后会自己主动的将其作为动态链接来处理。纯虚函数在继承类实现时,须要所有实现。

                     如public virtual function f()=0;

            注:下列函数不能被声明为虚函数:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。

          5. 执行时绑定和虚函数表:

                     动态绑定:绑定的对象是动态类型,其特性依赖于对象的动态特性。

                     虚函数表: C++的多态是通过动态绑定和虚函数表来实现的。这是虚函数的地址表,存储着函数在内存的地址。一般类的对象实例地址就是虚函数表。有虚函数覆盖时,虚函数的地址存储的是被覆盖的子类的函数的地址。

                    事实上知道虚函数表了,我们就能够通过操作指针的方式訪问一些额外的数据(当然假设直接写成代码会编译不通过的)。对于基类指针指向派生类时:我们能够通过内存訪问派生类未覆盖基类的函数,訪问基类的非public函数。

          6. 空类的大小不是零:由于为了确保两个实例在内存中的地址空间不同。

                        例如以下,A的大小为1,B由于有虚函数表的大小为4.

                              class A{};

                              class B:public virtual A{};

                       多重继承的大小也是1,例如以下:                              

                             class Father1{}; class Father2{};

                             class Child:Father1, Father2{};

          7. 深拷贝与浅拷贝:

                     C++会有一个默认的拷贝构造函数,将某类的变量所有赋值到新的类中。但是这样的情况有两个例外:(1). 类的默认构造函数做了一些额外的事情,比方使用静态数据统计类被初始化的次数(此时浅拷贝也无法解决这个问题,须要额外的改动拷贝构造函数)。 (2). 类内部有动态成员:此时发生拷贝时就须要对类内部的动态成员又一次申请内存空间,然后将动态变量的值拷贝过来。

           8. 仅仅要在基类中使用了virtualkeyword,那么派生类中不管是否加入virtual,都能够实现多态。

           9. 虚拟析构函数:在使用基类指针指向派生类时,假设未定义虚拟析构函数那么派生类的析构函数不会被调用,动态数据也不会被释放。

              构造函数和析构函数的调用顺序为:先基类构造函数,再派生类构造函数。析构时,先派生类析构函数,再基类析构函数。

           10. public,protected, private权限:privated:类成员函数、友元函数。 protected:派生类、类成员函数、友元函数,public全部

           11.  可重入函数(多线程安全),即该函数能够被中断,就意味着它除了自己栈上的变量以外不依赖不论什么环境。

           12.  操作系统内存管理有堆式管理、页式管理、段式管理和段页式管理。堆式管理把内存分为一大块一大块的,所需程序片段不在主村时就分配一块主存程序,把程序片段load入主存。页式管理:把主页分为一页一页的,每一页的空间都比块小非常多。段式管理:把主存分为一段一段的,每一段的空间比一页一页的空间小非常多。段页式管理。

三、基础的数据结构编程知识:

           事实上我理解作为面试来说,通常会面时应聘者的基础知识,假设对于过于复杂的图程序,动态规划一般不太适合。而对于数组、链表、二叉树、字符串处理的考察,既能考察出应聘者的基础知识、代码风格、命名书写规范,又能考察出应聘者的代码是否包括bug。而比較深入的一些题目一般仅仅作为脑筋急转弯或者给出思路,博主在此提醒各位,面试时准确性更加重要,假设一时想不出来,能够首先给出一个效率较低的实现,然后再一步步优化。以下分析一下对于数组、链表、二叉树和字符串处理的常考题目:
          1. 数组:一般到了数组就会考察排序(稳定性、平均、最好、最差时间复杂度/内存使用、不同情况下那种最快)、二分的特性。
                 2. 链表:链表的插入,逆序,推断链表相交,找到链表交点,链表的删除。
          3. 二叉树:二叉树的深度、前序/中序/后序遍历、二叉树的叶子数目、二叉树的节点数目、二叉树的层次遍历。
          4. 字符串的处理:atoi,itoa,字符串替换、字符串的查找。再次特意提醒一下,字符串处理设计指针的处理所以指针的内存訪问控制,须要使用很多其它的注意,比方參数为空,内存申请失败,返回值控制,指针加减。
   
          1. 数组:
                 1).  排序:各种排序算法的对照方下图所看到的
排序法平均时间最差情形稳定度额外空间备注
冒泡O(n2)    O(n2)稳定O(1)n小时较好
交换    O(n2)    O(n2)不稳定O(1)n小时较好
选择O(n2)O(n2)不稳定O(1)n小时较好
插入O(n2)O(n2)稳定O(1)大部分已排序时较好
基数O(logRB)O(logRB)稳定O(n)

B是真数(0-9),

R是基数(个十百)

ShellO(nlogn)O(ns) 1<s<2不稳定O(1)s是所选分组
高速O(nlogn)O(n2)不稳定O(nlogn)n大时较好
归并O(nlogn)O(nlogn)稳定O(1)n大时较好
O(nlogn)O(nlogn)不稳定O(1)n大时较好

                    2) 详细的每种排序算法的实现:
                     a) 高速排序实现:
int partition(int a[],int low,int high){
    int data=a[low],tem;
    int i=low+1,j=low+1;

    while(j<=high){
        if(a[j]>data)
            j++;
        else{
            tem=a[i];
            a[i]=a[j];
            a[j]=tem;
            i++;
            j++;
        }
    }
    a[low]=a[i-1];
    a[i-1]=data;
    return i-1;

}


void qsort1(int a[],int low,int high){
    if(low>=high)
        return;
    int mid=partition(a,low,high);
    if(mid-1>low)
        qsort1(a,low,mid-1);
    if(high>mid+1)
        qsort1(a,mid+1,high);

}
                     b) 堆排序实现:
void HeapModify(int a[], int i, int length){
    int j=-1;
    if( i*2+1 < length ){
        if( a[i*2+1]>a[i]){
            j=i*2+1;
        }
    }
    if( i*2 +2 < length){
        if( a[i*2 +2] > a[i] && a[2*i+2] > a[2*i+1]){
                j=i*2+2;
        }
    }

    if( j> 0){
        int tem;
        tem=a[i];
        a[i]=a[j];
        a[j]=tem;
        HeapModify(a,j,length);
    }
}
void Heap_sort(int a[],int length){
    int i,tem;
    for(i=length/2;i>=0;i--){
        HeapModify(a,i,length);
    }

    for(i=0;i<length-1;i++){
        tem=a[0];
        a[0]=a[length-1-i];
        a[length-1-i]=tem;
        HeapModify(a,0,length-i-1);
    }
}
                    
                     c ) 最大子数组求和:
int Max_Sum(const int a[],const int length_a){
    int length,i,max1=0;
    int *b=new int[10];

    max1=b[0]=a[0];
    for(i=1;i<length_a;i++){
        b[i]=max(b[i-1]+a[i],a[i]);
        if(b[i]>max1){
            max1=b[i];
        }
    }
    return max1;
}

3) 字符串处理程序:
                      a). atoi实现:
int atoi1(char *str){
    assert(str!=NULL);
    int result=0,pos=1;
    if(*str=='-'){
        pos=-1;
        str++;
    }
    while(*str!='\0'){
        assert(*str<='9'&&*str>='0');
        result=result*10+*str-'0';
        str++;
    }
    return result*pos;

}
                      b). itoa实现:
char *itoa1(int num,int k){
    char data[]="0123456789ABCDEF";
    bool pos=true;
    int count=1,tem;
    int num1=num;
    if(num<0){
        num=-num;
        pos=false;
        count++;
    }
    while(num>0){
        num=num/k;
        count=count+1;
    }
    char *result=new char[count];
    char *presult=result,*pstr;
    if(pos==false){
        *presult++='-';
    }
    pstr=presult;

    while(num1>0){
        *presult++=data[num1%k];
        num1=num1/k;
    }
    *presult--='\0';
    while(pstr<presult){
        tem=*pstr;
        *pstr=*presult;
        *presult=tem;
        *presult--;
        *pstr++;
    }

    return result;
}
                      c). 将字符串空格替换为目的串的实现:
char * Replace_Str(char * str, char * target){
    assert(str!=NULL&&target!=NULL);
    char *pstr=str;
    int count=0;
    while(*pstr!='\0'){
        if(*pstr==' '){
            count++;
        }
        pstr++;
    }
    char * result=new char[sizeof(str)+count*strlen(target)];
    char *presult=result;
    for(pstr=str;pstr!='\0';pstr++){
        if(*pstr!=' '){
            *presult=*pstr;
        }
        else{
            char *ptarget=target;
            while(*ptarget!='\0'){
                *presult++=*ptarget++;
            }
        }
    }
    *presult='\0';
    return result;
}
                     d). strcpy实现:
void strcpy1(char * source, char * dest){
    assert(source!=NULL&&dest!=NULL);
    char * psource=source, *pdest=dest;
    while(*psource!='\0'){
        *pdest++=*psource++;
    }
    *pdest='\0';
}
                     e). 查找目的串第一次出现位置(最快的肯定是KMP O(M+N)),或者查找目的串在源串中的出现次数:
<span style="font-size:14px;">int Find_Str(const char* source,const char* target){ //查找目的串在源串中出现的位置,最弱的方法
    assert(source!=NULL&&target!=NULL);

    int i,j,i1;
    int len_source=strlen(source),len_target=strlen(target);

    for(i=0;i<len_source;i++){
        i1=i;
        for(j=0;source[i1]==target[j]&&j<len_target;i1++,j++);
        if(j==len_target)
            return i;
    }
    return -1;
}</span>
                     f). memcopy实现:
void *memcpy1(void * dest, const void * source, size_t num){//注意须要考虑内存重叠的部分,特别的dest在source数据区间内部时候的处理
    assert(dest!=NULL&&source!=NULL);
    int i;
    byte *psource=(byte *) source;
    byte *pdest=(byte *) dest;

    if(pdest>psource&&pdest<psource+num){
        for(i=num-1;i>=0;i--)
            *(pdest+i)=*(psource+i);
    }else{
        for(i=0;i<=num;i++)
            *(pdest+i)=*(psource+i);
    }

    return dest;
}


                     3) 链表处理:
                     a). 链表相交判定:
                     b). 链表逆序:
                     c). 两个有序链表拼接成为一个有序链表:
            4) 二叉树处理:
                     a). 二叉树的前序/后序/中序遍历:
                               a. 二叉树前序/非递归:
void Pre_Traverse_Recur(NODE *root){//前序递归
    if(root==NULL)
        return;
    cout<<root->data;
    Pre_Traverse_Recur(root->left);
    Pre_Traverse_Recur(root->right);
}
void Pre_Traverse(NODE *root){<span style="font-family: Arial, Helvetica, sans-serif;">//前序非递归</span>
    stack<NODE*> stack1;
    NODE *p=root;

    while(p!=NULL||!stack1.empty()){
        while(p!=NULL){
            cout<<p->data;
            stack1.push(p);
            p=p->left;
        }
        if(!stack1.empty()){
            p=stack1.top();
            stack1.pop();
            p=p->right;
        }
    }
}
                               b. 二叉树中序/非递归:
void Inorder_Traverse_Recur(NODE *root){//中序递归
    if(root==NULL)
        return;
    Pre_Traverse_Recur(root->left);
    cout<<root->data;
    Pre_Traverse_Recur(root->right);
}
void Inorder_Traverse(NODE *root){<span style="font-family: Arial, Helvetica, sans-serif;">//中序非递归</span>
    stack <NODE*> stack1;
    NODE *p=root;
    while(p!=NULL||!stack1.empty()){
        while(p!=NULL){
            stack1.push(p);
            p=p->left;
        }
        if(!stack1.empty()){
            p=stack1.top();
            cout<<p->data;
            stack1.pop();
            p=p->right;
        }
    }
}
                               c. 二叉树后序/非递归:
void Postorder_Traverse_Recur(NODE *root){//后序递归
    if(root==NULL)
        return;
    Postorder_Traverse_Recur(root->left);
    Postorder_Traverse_Recur(root->right);
    cout<<root->data;
}

#include <hash_map>
using namespace std;
using namespace __gnu_cxx;
void Postorder_Traverse(NODE *root){//非递归后序遍历
    stack <NODE *> stack1;
    hash_map <int,int> hashmap1;


    assert(root!=NULL);
    NODE *p=root;


    while(p!=NULL){
        stack1.push(p);
        p=p->left;
        hashmap1[stack1.size()]=0;
    }


    while(!stack1.empty()){
        p=stack1.top();


        while(p->right&&hashmap1[stack1.size()]==0){
            hashmap1[stack1.size()]=1;
            p=p->right;
            while(p!=NULL){
                stack1.push(p);
                hashmap1[stack1.size()]=0;
                p=p->left;
            }
            p=stack1.top();
        }
        p=stack1.top();
        cout<<p->data;
        stack1.pop();
    }
}
                     b). 二叉树的深度/节点数目/叶子数目:
                     c). 二叉树的两个节点的最短距离,最低公共祖先:
                     d). 全然二叉树判定:
                     e). 二叉树镜像:
            5) 其它:
                     a). B/B+树:
四、STL基础知识:
           1. STL:标准模板库,它由算法、迭代器、容器组成。
           2. STL容器简单介绍, STL容器大致能够分为三类:
                   1). 序列容器:vector, list, deque, string
                   2). 关联容器:map, set, hashmap, multiset, hash_set
                   3). 其它杂项:stack, queue, valarray, bitset   
           3. list使用:
    list<int> lst1;
    list<int>::iterator itor;
    lst1.push_front(1);
    lst1.push_back(2);
    int i=lst1.front();
    list1.size();
    list1.empty();
            4. stack使用:
    stack<int> stack1;
    stack1.push(10);
    int i=stack1.top();
    stack1.pop();
    stack1.size();
    stack1.empty();
            5. vector使用:
    vector<int> v;
    vector<int> ::iterator itor;
    for(int i=0;i<10;i++){
        v.push_back(i);
    }
    itor=v.begin();
    v.insert(itor,55);
    v.erase(itor);

    for(itor=v.begin();itor!=v.end();itor++){
        cout<<*itor<<endl;
    }
   vector<int>::iterator findit = find(v.begin(),v.end(),123);
    if(findit==v.end())
        cout<<"Not exist!!"<<endl;
            6. Map/HashMap使用:
    map <int,int> map1;
    map1[5]=5;
    map1.insert(pair<int,int>(7,7));
    map1.insert(pair<int,int>(1,1));
    map <int,int> ::iterator itor;

    for(itor=map1.begin();itor!=map1.end();itor++)
        cout<<itor->first<<itor->second<<endl;
    itor=map1.find(5);
    if(itor!=map1.end())
        cout<<"Exists"<<endl;
    map1.size();
    map1.empty();


 
  
五、多线程和网络编程:
           1. 原子锁 自旋锁 信号量 相互排斥锁:
                   自旋锁专门为多处理器并发而引入的一种锁,在内核中大量应用于中断处理等部分。最多仅仅能被一个内核任务持有,假设一个内核任务试图请求一个已经被占用的自旋锁,那么这个任务就会一直进行忙循环。
                   信号量:用在多线程、多进程同步,假设线程完毕了某个操作就会通过信号量告诉别的线程,别的线程在进行某些动作。进程假设检測到信号量被占用,就会被发送到等待序列。信号量能够不是为了资源竞争使用,比方控制流程。
                   相互排斥锁:用在多线程同步,如竞争资源訪问控制。
           2. C++多线程和多进程:
           3. TCP三次握手过程:
                            
           4. socket编程基础:
                 server端 socket, bind, listen, accept. client端 socket, connect , send/receive。
           5. 短连接和长连接:
                   长连接时,client端向server端发起连接,server接受连接,两方建立连接。假设client/server完毕一次读写以后,他们之间的连接并未主动关闭,兴许操作能够继续使用这个连接。
           6. 多线程编程基础:
                   多线程之间能够通过共享内存的方式同步数据,或者使用TCP。多线程间同步能够使用信号量、相互排斥锁、临界区、事件。
           7. TCP与UDP的差别:
                   TCP是面向连接的,可靠的字节流服务。两方先建立TCP连接,然后才数据传输。TCP支持超时重发,丢弃反复数据,校检数据和流量控制。ftp
                   UDP是非面向连接的,不提供可靠性。它仅仅是把应用程序传给IP层的数据报先发送出去,并不能保证它们可以达到目的地。没有超市重发等机制,故而传输速度非常快。ping
                   TCP报文头的结构:源port、目的port、序列号、确认序列号、首部长度、窗体大小、紧急指针、选项、数据。UDP数据包显然没有上述的非常多数据。
           8. OSI7层模型:
             
           9. C++实现单例模式:
class Singleton{//太令我挣扎了。。。
private:
    static Singleton *_instance;
    Singleton(){
    }
public:
    static Singleton * Instance(){
        if(0==_instance)
            _instance=new Singleton;
        return _instance;
    }
    static Singleton * Get_Instance(){
        return _instance;
    }
};

Singleton* Singleton::_instance = 0;
int main(){
    Singleton *sg = Singleton::Instance();
    return 0;
}
           10. C++异常处理:

參考文献:
1. 进程内存空间: http://www.cnblogs.com/biyeymyhjob/archive/2012/07/14/2591714.html
2. 原子操作、信号量、自旋锁、相互排斥锁: http://blog.163.com/hbu_lijian/blog/static/126129153201261722410353/
3. 二叉树面试题目:http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html
5. OSI 7层模型: http://blog.sina.com.cn/s/blog_6f83fdb40101fchk.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值