• 微软2013年笔试题详解及深入


    Microsoft

    下面哪些调用转换支持可变长度参数:

    A. cdecl  B. stdcall  C. pascal  D. fastcall

    几种函数调用方式:

             __cdecl C Declaration的缩写,表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。

    _stdcall StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。

    PASCAL Pascal语言的函数调用方式,也可以在C/C++中使用,参数压栈顺序与前两者相反。返回时的清栈方式与_stdcall相同。

    _fastcall是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。

    _thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecxBorlandC++编译器使用eax。返回方式和_stdcall相当。

    _fastcall _thiscall涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。

    C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。

    带有可变参数的函数必须且只能使用_cdecl方式,例如下面的函数:

    int printf(char * fmtStr, ...);

    int scanf(char * fmtStr, ...);

     

    以下代码的输出结果:

    class A

    {

    public:

             virtual void f()

             {

                       cout<<"A:f()"<<endl;

             }

            

    void f() const

             {

                       cout<<"A:f()const"<<endl;

             }

    };

     

    class B:public A

    {

    public:

             void f()

             {

                       cout<<"B:f()"<<endl;

             }

            

    void f() const

             {

                       cout<<"B:f()const"<<endl;

             }

    };

     

    void  ga(const A *a)

    {

    a->f();

    }

     

    int _tmain(intargc, _TCHAR* argv[])

    {

             A *a=new B();

             a->f();

             ga(a);

     

    }

    答案:B::f()A::f()const

    第一个,b->f()为动态绑定,输出B::f没问题

    第二个,由于函数ga的参数有const,所以调用成员函数也是调用const版本,但是const版本的不是虚函数,不存在动态绑定,所以输出A::f const

    const 修饰函数和没有const是不同的重载,对于非const对象调用非const函数,当然也可以调用const函数(优先前者);然而对于const对象则只能调用const函数;其次,A中的f ()函数标注为 virtual,那么子类 B 中跟它原模原样声明即使没有标注为 virtual的函数依然是 virtual 函数。(c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。)

    const是函数类型的一部分。虚函数重载的时候这个const也必须一致。如果基类的虚函数带有const,而在子类的实现中没有带const,则相当于在子类中重新定义了一个新的函数。

     

    定义虚函数的限制:

    1)非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。构造函数不能是虚函数,内联函数也不能是虚函数。构造函数不能是虚函数原因在于:首先构造函数构造一个对象时必须知道对象的实际类型,其次,虚函数的执行依赖于虚函数表,而虚函数表在构造函数中进行初始化工作。

    2)只需要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。

    3)当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数。

    4)如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中,也不能出现这种同名函数。

     

    子类中可以不重写父类中的虚函数:

    class A

    {

    public:

             virtual void f()

             {

                       cout<<"A:f()"<<endl;

             }

    };

    class B:public A

    {

    public:

             void f2()

             {

                       cout<<"B:f()"<<endl;

             }

    };

     

    int main(){

            A *a=new B();

             a->f();

    }

    输出结果:A:f()

    但如果基类中定义的是纯虚函数,而在子类中没有实习,那么在这个子类中,该函数仍然是纯虚函数,且该子类也不能被实例化。

     

    线程与进程

    程序与进程的关系:程序是计算机指令的集合,它以文件的形式存储在磁盘上,而进程通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。一个程序可以对应多个进程,同时,在一个进程中也可以同时访问多个程序。

    进程是资源申请、调度和独立运行的单位,因此它使用系统中的运行资源。程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此它不占用系统的运行资源。

    进程与线程的关系:

    1)进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。也就是说,进程实际上是线程的执行环境。

    2)单个进程可能包含若干个线程,这些线程都“同时”(时间片)执行进程地址空间中的代码。每个进程至少拥有一个线程。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程,也就是执行主函数的线程。此后主线程可以创建其他线程。

    3)进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,子进程和父进程有不同的代码和数据空间。而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,多个线程则共享数据空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮。

    4)在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

     

    以下代码的输出结果

    int x=10;

    x= x++;

    printf("%d /n", x);

    不同编译器编译出的结果不同。可能为10DevC++),也可能为11VC6.0.

     

    以下const用法正确的是:

    A. const int a; // a 是常数

    B. int const a; // a 是常数

    C. int const *a; // a 指向常数的指针

    D. const int *a; // a 是常指针

    E. int const *a; // a 是常指针

    答案:A B C

    注:D E都是指向常量的指针,而 int * const a; 才是常量指针。约束指针还是约束常量主要看*的位置。

    代码验证:

             const int a=10;

             int const b=11;

             cout<<a<<" "<<b<<endl;

    输出:10 11

     

    为什么下面的例子在使用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?

    const int n = 5;

    int a[n];

    答案与分析:

    1)这个问题讨论的是“常量”与“只读变量”的区别。常量,例如5 "abc",等,肯定是只读的,因为常量是被编译器放在内存中的只读区域,当然也就不能够去修改它。而“只读变量”则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。C语言关键字const就是用来限定一个变量不允许被改变的修饰符(Qualifier)。上述代码中变量n被修饰为只读变量,可惜再怎么修饰也不是常量。而ANSI C规定数组定义时长度必须是“常量”,“只读变量”也是不可以的,“常量”不等于“不可变的变量”。

    2)但是在标准C++中,这样定义的是一个常量,这种写法是对的。实际上,根据编译过程及内存分配来看,这种用法本来就应该是合理的,只是ANSI C对数组的规定限制了它(实际上用GCCVS2005编译以上代码,确实没有错误产生,也没有给出警告)。

    3)那么,在ANSI C中用什么来定义常量呢?答案是enum类型和#define宏,这两个都可以用来定义常量。

     

    例:下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?

    typedef char * pStr;

    char string[4] = "abc";

    const char *p1 = string; //1

    const pStr p2 = string; //2

    p1++;

    p2++;

    答案与分析:

    问题出在p2++上。

    1const使用的基本形式: const type m;

    限定m不可变。

    2)替换基本形式中的m1式中的*p1,替换后const char *p1;

    限定*p1不可变,当然p1是可变的,因此问题中p1++是对的。

    3)替换基本形式中的type2式中的pStr,替换后const pStr m;

    限定m不可变,题中的pStr就是一种新类型,因此问题中p2不可变,p2++是错误的。

     

    下面分别用const限定不可变的内容是什么?

    1const在前面

    const int nValue //nValueconst

    const char *pContent; //*pContentconst, pContent可变

    const char* const pContent; //pContent*pContent都是const

    2const在后面,与上面的声明对等

    int const nValue; //nValueconst

    char const * pContent; //*pContentconst, pContent可变

    char* const pContent; //pContentconst,*pContent可变

    char const* const pContent; //pContent*pContent都是const

    答案与分析:

    const和指针一起使用是C语言中一个很常见的困惑之处,在实际开发中,特别是在看别人代码的时候,常常会因为这样而不好判断作者的意图,下面讲一下我的判断原则:

    const只修饰其后的变量,至于const放在类型前还是类型后并没有区别。如:const int aint const a都是修饰aconst*不是一种类型,如果*pType之前是某类型,那么pType是指向该类型的指针

    一个简单的判断方法:指针运算符*,是从右到左,那么如:char const * pContent,可以理解为char const (* pContent),即* pContentconst,而pContent则是可变的。

     

    int const * p1,p2;

    p2const(*p1)是一整体,因此(*p1)const,但p1是可变的。int * p1,p2只代表p1是指向整型的指针,要表示p1p2都是指针是需写成int * p1,* p2。所以无论是* const p1,p2还是const * p1,p2,里面的*都是属于p1的。

     

    int const * const p1,p2;

    p2const,是前一个const修饰的,*p1也被前一个const修饰,而p1被后一个const修饰。

     

    int * const p1,p2;

    p1const,(* const p1)是整体,所以const不修饰p2

     

    const*的左边,则指针指向的变量的值不可变;在*的右边,则指针的指向不可变。简记为“左定值,右定向”。

    1)指针指向的变量的值不能变,指向可变

    int x = 1;

    int y = 2;

    const int* px = &x;

    int const* px = &x; //这两句表达式一样效果

    px = &y; //正确,允许改变指向

    *px = 3; //错误,不允许改变指针指向的变量的值

    2)指针指向的变量的值可以改变,指向不可变

    int x = 1;

    int y = 2;

    int* const px = &x;

    px = &y; //错误,不允许改变指针指向

    *px = 3; //正确,允许改变指针指向的变量的值

    3)指针指向的变量的值不可变,指向不可变

    int x = 1;

    int y = 2;

    const int* const px = &x;

    int const* const px = &x;

    px = &y; //错误,不允许改变指针指向

    *px = 3; //错误,不允许改变指针指向的变量的值

    附:

    c中,对于const定义的指针,不赋初值编译不报错,

    int* const px;等不会报错。

    但是,在C++

    int* const px;const int* const px;会报错,const int* px;不报错。

    必须初始化指针的指向int* const px = &x;const int* const px=&x;

    强烈建议在初始化时说明指针的指向,防止出现野指针

     

    1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡至少要多少只小白鼠才能在24小时鉴别出哪瓶水有毒。

    1000个瓶分别标上如下标签(10位长度):

    0000000001 (第1瓶)

    0000000010 (第2瓶)

    0000000011 (第3瓶)

    ......

    1111101000 (第1000瓶)

    从编号最后1位是1的所有的瓶子里面取出1滴混在一起(比如从第一瓶,第三瓶,里分别取出一滴混在一起)并标上记号为1。以此类推,从编号第一位是1的所有的瓶子里面取出1滴混在一起并标上记号为10。现在得到有10个编号的混合液,小白鼠排排站,分别标上109…1号,并分别给它们灌上对应号码的混合液。24小时过去了,过来验尸:

    从左到右,死了的小白鼠贴上标签1,没死的贴上0,最后得到一个序号,把这个序号换成10进制的数字,就是有毒的那瓶水的编号。

     

    3*4的格子有几个矩形:

    M*N网格中有横竖各M+1N+1条直线,其中,任意各取两条都可以组成一个长方形。

    C(4,2)*C(5,2)=6*10=60;

    A(N,N)=N!

    A(N,M)=N*(N-1)*…*(N-M+1)

    C(N,M)=A(N,M)/A(M,M)

     

    一条线把平面分成两块,两条线把平面分成四块,如果任意两条线不平行,且没有3条线交在同一点,问100条线将平面分成多少块。

    答案:5051

    1条直线最多将平面分成2个部分;2条直线最多将平面分成4个部分;3条直线最多将平面分成7个部分;现在添上第4条直线.它与前面的3条直线最多有3个交点,这3个交点将第4条直线分成4段,其中每一段将原来所在平面部分一分为二,所以4条直线最多将平面分成7+4=11个部分.

    完全类似地,5条直线最多将平面分成11+5=16个部分;6条直线最多将平面分成16+6=22个部分;7条直线最多将平面分成22+7=29个部分;8条直线最多将平面分成29+8=37个部分.

    一般地,n条直线最多将平面分成2+2+3....+N=N*N+N+2/2

     

    N个球,其中只有一个是重量较轻的,用天平只称三次就能找到较轻的球,以下的N值哪个是可能的?

    A 12

    B 16

    C 20

    D 24

    E 28

    3 个一次可以测出来,3*3 = 9个以内 2 次,3*3*3 = 27个以内,3次!

     

    下列代码的输出是什么?

    #include <iostream>

    using namespace std;

    class A{

    public:

        long a;

    };

     

    class B : public A

    {

    public:

        long b;

    };

     

    void seta(A* data, int idx)

    {

        data[idx].a = 2;  // 形参为A的指针。数组步长按A的大小来取。

    }

     

    int main(int argc, char *argv[])

    {

        B data[4];

     

        for(int i=0; i<4; ++i){

            data[i].a = 1;

            data[i].b = 1;

            seta(data, i);

        }

     

            cout<<sizeof(A)<<endl; // 4

             cout<<sizeof(B)<<endl; // 8

        for(int i=0; i<4; ++i){

            std::cout  << data[i].a << data[i].b;

    }

    }

    输出为22221111

    附:

    struct Test{

        short int a;

        short int b;

    };

     

    int main(int argc, char *argv[])

    {

        int test = 0x12345678;

        printf("%x %x", (*(struct Test*)&test).a, (*(struct Test*)&test).b);    // 5678 1234

    }

    //Intel是小端处理器

  • 相关阅读:
    ASP.NET MVC5 ModelBinder
    19 个 JavaScript 编码小技巧
    Tomcat使用线程池配置高并发连接
    排名前16的Java工具类
    RabbitMQ与spring集成,配置完整的生产者和消费者
    Spring4+Springmvc+quartz实现多线程动态定时调度
    Redis优化建议
    JPA的多表复杂查询
    Spring Boot中自定义注解+AOP实现主备库切换
    Restful API 中的错误处理
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3170184.html
Copyright © 2020-2023  润新知