微软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是小端处理器

原文地址:https://www.cnblogs.com/jiangu66/p/3170184.html