美文网首页
《程序员面试宝典》笔记

《程序员面试宝典》笔记

作者: 李相赫的乐芙兰 | 来源:发表于2019-03-21 14:23 被阅读0次

    过了一遍《宝典》,准备面人( ⚆ _ ⚆ )……
    一面的主要目标是判断这个人是否符合入职的条件,包括:
    1、技术能力是否过关(要能符合其工作年限)。
    2、沟通、表达、性格方面有没有异常的地方。
    3、潜力如何。
    4、是否热爱程序/做游戏。

    语言基础

    int i=1;
    void main(){
        int i=i;///i变量从声明的那一刻就是可见的,但是一个不确定的值
    }
    
    *(ptr++)+=123 应为*ptr=*ptr+123;ptr++ 先赋值,后移动指针
    
    printf计算参数是是从右到左压栈的
    printf("%d,%d\n",*ptr,*(++ptr)); 先是*(++ptr)
    
    
    前++和后++它们都是单目运算符,只需要一个操作数,但操作数只能是变量,不能是常量或表达式
    int x = 1;
    ++x++;///错误
    (++x)++;///正确
    ++(x++);///错误
    

    c++中调用被c编译器编译后的函数,为什么要加extern "C"
    c++语言支持函数重载,c语言不支持重载,函数被c++编译后在库中的名字与c语言不同
    void foo(int x,int y)被c编译器编译后名字为_foo,而c++编译器产生的名字是_foo_int_int
    c++提供了c连接交换指定符号extern "C"解决名字匹配问题

    如果const位于星号左侧(无视类型的位置),则const就是用来修饰指针所指向的变量,即不能通过指针来修改指向的变量的值
    如果const位于星号右侧,则const就是修饰指针本身,即不能改变指针的指向

    const加在成员函数之后,表示成员函数不能修改类的成员变量,也不能调用其他能引起数据成员变化的成员函数
    函数实现时也必须加上const,否则编译器会把它看成另一个函数

    如果把const加在成员函数之前,意味着函数的返回值是常量

    char* ss1="0123456789";///sizeof(ss1)=4 指针类型
    char ss2[]="0123456789";///sizeof(ss2)=11   数组类型
    char ss3[100]="0123456789";///sizeof(ss3)=100
    int ss4[100];///sizeof(ss4)=400
    
    char ss[10];
    int test(char ss[]){
        return sizeof(ss);///4,ss[]作为参数退化成char*
    }
    

    编译器会对结构进行对齐处理
    字节对齐是编译时决定的,运行时不会再发生变化
    pack指令可以禁止对齐调整

    静态变量是存放在全局数据区的,而sizeof计算栈中分配的大小,是不会计算在内的

    空类占空间为1;单一继承、多重继承占空间也是1;虚继承有虚表,占空间为4

    内联函数不需要中断调用
    关键字inline必须与函数定义放在一起才会是成员称为内联,放在声明处没有用

    char *strA(){
    char str[] = "hello world";
    return str;
    }
    错误,局部数组局部变量,只存在于函数的栈帧中

    const char* strA(){
    char* str ="hello world";
    return str;
    }
    正确,字符串常量保存在只读数据段

    const char* strA(){
    static char str[] = "hello world";
    return str;
    }
    正确,通过static开辟一段静态存储空间

    int p,q;
    p-q的结果是(addr(p)-addr(q))/sizeof(int)

    class A{
    public:
        A(){m_a=1;m_b=2;}
        void fun(){printf("%d%d",m_a,m_b);}
    private:
        int m_a,m_b;
    };
    class B{
    public:
        B(){m_c=3;}
        void fun(){printf("%d",m_c);}
    private:
        int m_c;
    }
    
    void main(){
        A a;
        B *pb=(B*)(&a);
        pb->fun();///调用B::fun(),但是打印的是A中与m_c相对B偏移量相同的m_a的值
    }
    

    语言技巧

    判断一个数是否是2的N次方 !(x&(x-1))

    int f(int x,int y){
    return (x&y)+((x^y)>>1);
    }
    求x,y的平均数

    不用+实现两数相加

    int Add(int a,int b){
        if(b==0) return a;
        int sum=a^b;
        int carry=(a&b)<<1;
        return Add(sum,carry);
    }
    

    求两数的较大值
    int max=((a+b)+abs(a-b))/2;

    bool flag=a>b;
    int max=flaga+(1-flag)b;

    F(0)=1,F(1)=1,
    F(n)=F(n-1)+F(n-2)
    求F(1025)%5;
    F(n)=F(n-1)+F(n-2)
    =2F(n-2)+F(n-3)
    =3F(n-3)+2F(n-4)
    =5F(n-4)+3F(n-5)
    所以F(n)%5 = 3F(n-5)%5
    又F(5)=5,所以F(1025)%5=0

    面向对象

    类的常量成员变量必须在构造函数中初始化,或者将其设置成static
    class A{
    const int a = 0;///错误
    };

    class A{
    A(){const int a = 0;}///正确
    };

    class A{
    static const int a = 0;///正确
    }

    子类重新定义父类虚函数的做法称为“覆盖”或者“重写”
    允许多个同名函数,而函数的参数表不同则是“重载”
    重载是静态的(编译时确定)
    覆盖是父类指针根据赋给它的不同子类指针动态去调用的
    重载不是多态

    多态性是允许你将父对象设置成为和一个或者更多的它的子对象相等的技术
    赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作
    多态的本质就是将子类类型的指针赋值给父类类型的指针

    友元不是成员函数,但是它可以方位类中的私有成员。友元的作用在于提高程序的运行效率,但是破换了类的封装性和隐蔽性

    构造函数详见这里

    操作系统

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

    栈:在函数调用时,第一个进栈的是函数调用语句的下一条可执行语句的地址,然后是函数的各个参数(参数一般是由右往左入栈),然后是函数中的局部变量。注意静态变量是不入栈的。
    堆:在操作系统中有一个记录空间内存地址的链表,分配一块内存时会在空间的首地址出记录本次分配的大小

    new和malloc的区别
    0.属性
    new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。
    1.参数
    使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
    2.返回类型
    new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
    3.分配失败
    new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
    4.自定义类型
    new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
    malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
    5.重载
    C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。
    6.内存区域
    new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

    句柄:32为的uint,windows系统用来标记系统资源在内存中的地址(系统资源的地址不稳定)

    auto_ptr不能放入容器中,当auto_ptr被复制时,原来的那一份会被删除

    This指针本质是成员函数的第一个参数,且为const类型(只读),只能在成员函数中使用。全局函数、静态函数都不能使用this

    相关文章

      网友评论

          本文标题:《程序员面试宝典》笔记

          本文链接:https://www.haomeiwen.com/subject/oriqvqtx.html