面试

来源:互联网 发布:淘宝上哪个冒菜好吃 编辑:程序博客网 时间:2024/05/22 13:14

第一组

1.烧一根不均匀的绳,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?

1)三根绳,第一根点燃两端,第二根点燃一端,第三根不点

 

2)第一根绳烧完(30分钟),点燃第二根绳的另一端,第二根绳烧完(45分钟),点燃第三根绳子两端,第三根绳烧完(1小时15),计时完成

 

 

2.你有一桶果冻,其中有黄色、绿色、红色三种,闭上眼睛抓取同种颜色的两个。抓取多少个就可以确定你肯定有两个同一颜色的果冻?

2)根据抽屉原理,4

 

3.如果你有无穷多的水,一个3公升的提捅,一个5公升的提捅,两只提捅形状上下都不均匀,问你如何才能准确称出4公升的水?

3)3升装满;3-5(全注入);3升装满;3-5(1);5升倒掉;3-5(注入1);3升装满;3-5;完成(:可用回溯法编程求解)

 

 

4.一个岔路口分别通向诚实国和说谎国。来了两个人,已知一个是诚实国的,另一个是说谎国的。诚实国永远说实话,说谎国永远说谎话。现在你要去说谎国,但不知道应该走哪条路,需要问这两个人。请问应该怎么问?

问其中一人:另外一个人会说哪一条路是通往诚实国的?回答者所指的那条路必然是通往说谎国的。

 

6.在9个点上画10条直线,要求每条直线上至少有三个点?

 

 

7.在一天的24小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?都分别是什么时间?你怎样算出来的?

23,因为分针要转24,时针才能转1,而分针和时针重合两次之间的间隔显然>1小时,它们有23次重合机会,每次重合中秒针有一次重合机会,所以是23

 

重合时间可以对照手表求出,也可列方程求出

 

第三组

 

1.你让工人为你工作7天,回报是一根金条,这个金条平分成相连的7段,你必须在每天结束的时候给他们一段金条。如果只允许你两次把金条弄断,你如何给你的工人付费?

1.) 分成1,2,4三段,第一天给1,第二天给2取回1,3天给1,4天给4取回12,5天给1,6天给2取回1,第七天给1

 

2.有一辆火车以每小时15公里的速度离开北京直奔广州,同时另一辆火车每小时20公里的速度从广州开往北京。如果有一只鸟,以30公里每小时的速度和两辆火车同时启动,从北京出发,碰到另一辆车后就向相反的方向返回去飞,就这样依次在两辆火车之间来回地飞,直到两辆火车相遇。请问,这只鸟共飞行了多长的距离? 

求出火车相遇时间,鸟速乘以时间就是鸟飞行的距离

 

 

3.你有四个装药丸的罐子,每个药丸都有一定的重量,被污染的药丸是没被污染的药丸的重量+1。只称量一次,如何判断哪个罐子的药被污染了?

四个罐子中分别取1,2,3,4颗药丸,称出比正常重多少,即可判断出那个罐子的药被污染

 

4.门外三个开关分别对应室内三盏灯,线路良好,在门外控制开关时候不能看到室内灯的情况,现在只允许进门一次,确定开关和灯的对应关系?

三个开关分别:,,10分钟,然后进屋,暗且凉的为开关1控制的灯,亮的为开关2控制的灯,暗且热的为开关3控制的灯

 

5.人民币为什么只有1、2、5、10的面值? 因为可以用1,2,5,10组合成任何需要的货币值,日常习惯为10进制

 

第四组

第一题 . 五个海盗抢到了100颗宝石,每一颗都一样大小和价值连城。他们决定这么分:

抽签决定自己的号码(1、2、3、4、5)

 

首先,由1号提出分配方案,然后大家表决,当且仅当超过半数的人同意时,按照他的方案

进行分配,否则将被扔进大海喂鲨鱼 如果1号死后,再由2号提出分配方案,然后剩下的4人进行表决,当且仅当超过半数的人同意时,按照他的方案进行分配,否则将被扔入大海喂鲨鱼

 

依此类推 条件:每个海盗都是很聪明的人,都能很理智地做出判断,从而做出选择。

 

问题:第一个海盗提出怎样的分配方案才能使自己的收益最大化?

第一题:970 1 2 0或者97 0 1 0 2 (提示:可用逆推法求出)

 

 

第二题 . 一道关于飞机加油的问题,已知:

 

每个飞机只有一个油箱, 飞机之间可以相互加油(注意是相互,没有加油机) 一箱油可供一架飞机绕地球飞半圈,

问题:

为使至少一架飞机绕地球一圈回到起飞时的飞机场,至少需要出动几架飞机?(所有飞机从同一机场起飞,而且必须安全返回机场,不允许中途降落,中间没有飞机场)

第二题:3架飞机5架次,飞法:

 

ABC 3架同时起飞,1/8,CAB加满油,C返航,1/4,BA加满油,B返航,A到达1/2,C从机场往另一方向起飞,3/4,C同已经空油箱的A平分剩余油量,同时B从机场起飞,AC7/8处同B平分剩余油量,刚好3架飞机同时返航。所以是3架飞机5架次。

 

第三题. 汽车加油问题

一辆载油500升的汽车从A开往1000公里外的B,已知汽车每公里耗油量为1升,A处有无穷多的油,其他任何地点都没有油,但该车可以在任何地点存放油以备中转,问从A到B最少需要多少油

第三题:需要建立数学模型

(提示,严格证明该模型最优比较麻烦,但确实可证,大胆猜想是解题关键)

题目可归结为求数列an=500/(2n+1) n=0,1,2,3......的和Sn什么时候大于等于1000,解得n> 6

n=6,S6=977.57

所以第一个中转点离起始位置距离为1000-977.57=22.43公里

所以第一次中转之前共耗油22.43*(2*7+1)=336.50

此后每次中转耗油500

所以总耗油量为7*500+336.50=3836.50

 

第五题. 推理游戏

 

教授选出两个从2到9的数,把它们的和告诉学生甲,把它们的积告诉学生乙,让他们轮流猜这两个数

 

甲说:“我猜不出”

 

乙说:“我猜不出”

 

甲说:“我猜到了”

 

乙说:“我也猜到了”

 

问这两个数是多少

 

第六题. 病狗问题

 

一个住宅区内有100户人家,每户人家养一条狗,每天傍晚大家都在同一个地方遛狗。已知这些狗中有一部分病狗,由于某种原因,狗的主人无法判断自己的狗是否是病狗,却能够分辨其他的狗是否有病,现在,上级传来通知,要求住户处决这些病狗,并且不允许指认他人的狗是病狗(就是只能判断自己的),过了7天之后,所有的病狗都被处决了,问,一共有几只病狗?为什么?

第六题:7(数学归纳法证明)

 

1)若只有1只病狗,因为病狗主人看不到有其他病狗,必然会知道自己的狗是病狗(前提是一定存在病狗),所以他会在第一天把病狗处决。

 

2)设有k只病狗的话,会在第k天被处决,那么,如果有k+1,病狗的主人只会看到k只病狗,而第k天没有人处决病狗,病狗主人就会在第k+1天知道自己

的狗是病狗,于是病狗在第k+1天被处决

 

3)1)2),若有n只病狗,必然在第n天被处决

 

 

第七题. U2合唱团在17分钟内得赶到演唱会场,途中必需跨过一座桥,四个人从桥的同一端出发,你得帮助他们到达另一端,天色很暗,而他们只有一只手电筒。一次同时最多可以有两人一起过桥,而过桥的时候必须持有手电筒,所以就得有人把手电筒带来带去,来回桥两端。手电筒是不能用丢的

方式来传递的。四个人的步行速度各不同,若两人同行则以较慢者的速度为准。BONO需花1分钟过桥,EDGE需花2分钟过桥,ADAM需花5分钟过桥,LARRY需花10分钟过桥,他们要如何在17分钟内过桥呢?

 

第八题. 监狱里有100个房间,每个房间内有一囚犯。一天,监狱长说,你们狱房外有一电灯,你们在放风时可以控制这个电灯(熄或亮)。每天只能有一个人出来放风,并且防风是随机的。如果在有限时间内,你们中的某人能对我说:“我敢保证,现在每个人都已经至少放过一次风了。”我就放了你们!问囚犯们要采取什么策略才能被监狱长放掉?如果采用了这种策略,大致多久他们可以被释放?

第八题:

 

约定好一个人作为报告人(可以是第一个放风的人)

 

规则如下:

 

1、报告人放风的时候开灯并数开灯次数

 

2、其他人第一次遇到开着灯放风时,将灯关闭

 

3、当报告人第100次开灯的时候,去向监狱长报告,要求监狱长放人......

 

按照概率大约30年后(10000)他们可以被释放

 

 

typedef struct LNode

{

    int data;

    struct LNode *link;

}LNode,*LinkList;

 

voidJOSEPHUS(int n,int k,int m) //n为总人数,k为第一个开始报数的人,m为出列者喊到的数

{

    /* p为当前结点  r为辅助结点,指向p的前驱结点  list为头节点*/

    LinkListp,r,list;

    /*建立循环链表*/

    for(int i=0,i<n,i++)

    {

        p=(LinkList)malloc(sizeof(LNode));

        p->data=i;

        if(list==NULL)

            list=p;

        else

            r->link=p;

        r=p;   }

    p>link=list; /*使链表循环起来*/

    p=list; /*使p指向头节点*/

    /*把当前指针移动到第一个报数的人*/

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

    {       r=p

        p=p->link;    }

    /*循环地删除队列结点*/

    while(p->link!=p)

    {

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

        {

            r=p;

            p=p->link;

        }

        r->link=p->link;

        printf("被删除的元素:%4d ",p->data);

        free(p);

        p=r->link;

    }

    printf("\n最后被删除的元素是:%4d",P->data);

}

 

 

 

class Number {
public:
string type;
Number(): type(“void”) { }
explicit Number(short) :type(“short”) { } //这里显式构造,防止隐式转换.
Number(int) : type(“int”) { }
};
void Show(const Number& n) { cout << n.type; }
void f()
{
short s = 42;
Show(s);        //这里首先需要对s的数据类型自动转换到int型,所以使用Number(int)这个构造函数
//结果答案为C.
}
a) void
b) short
c) int
d) None of the above

 

 

1.
What is displayed when f() is called given the code:
class Number {
public:
string type;

Number(): type(“void”) { }
explicit Number(short) : type(“short”) { }
Number(int) : type(“int”) { }
};
void Show(const Number& n) { cout << n.type; }
void f()
{
short s = 42;
Show(s);
}
a) void
b) short
c) int
d) None of the above

2. Which is the correct output for the following code
double dArray[2] = {4, 8}, *p, *q;
p = &dArray[0];
q = p + 1;
cout << q – p << endl;
cout << (int)q – (int)p << endl;
a) 1 and 8
b) 8 and 4
c) 4 and 8
d) 8 and 1

第一个选C;
虽然传入的是short类型,但是short类型的构造函数被生命被explicit,也就是只能显示类型转换,不能使用隐式类型转换。
第二个选A;
第一个是指针加减,按照的是指向地址类型的加减,只跟类型位置有关,q和p指向的数据类型以实际数据类型来算差一个位置,因此是1。而第二个加减是实际指针值得加减,在内存中一个double类型占据8个字节,因此是8

 

 

1.  main()  

2.  {  

3.  union/*定义一个联合*/  

4.  int i;  

5.  struct/*在联合中定义一个结构*/  

6.  char first;  

7.  char second;  

8.  }half;  

9.  }number;  

10.   number.i=0x4241; /*联合成员赋值*/  

11.   printf("%c%c\n",number.half.first, mumber.half.second);  

12.   number.half.first='a'/*联合中结构成员赋值*/  

13.   number.half.second='b';  

14.   printf("%x\n", number.i);  

15.   getch();  

16.   }  

main()

{

union{ /*定义一个联合*/

int i;

struct{ /*在联合中定义一个结构*/

char first;

char second;

}half;

}number;

number.i=0x4241; /*联合成员赋值*/

printf("%c%c\n",number.half.first, mumber.half.second);

number.half.first='a'; /*联合中结构成员赋值*/

number.half.second='b';

printf("%x\n", number.i);

getch();

}

答案: AB (0x41对应'A',是低位;0x42对应'B',是高位)。

a=0x61, b=0x62,按照低位低地址,高位高地址的原则,得答案0x6261 (number.inumber.half共用一块地址空间)。

 

1设计模式:

整全两模块;

只要用另一模块的一部分,屏蔽其它功能;

有多种类,有类似的信息,扩展、复用差;

两个类的变数之间建立联系;

 

2EXE中可以释放DLL中的申请的内存吗?

3.delete Delete[]的区别;

4. Explicit

5. Com的三个基本接口。

 

 

 

动态链接库、静态库、import库区别

 

动态链接库(DynamicLinked Library):

Windows为应用程序提供了丰富的函数调用,这些函数调用都包含在动态链接库中。其中有3个最重要的DLL,

Kernel32.dll它包含用于管理内存、进程和线程的各个函数;

 

User32.dll它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。

 

静态库(Static Library):

函数数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块

 

组合起来创建最终的可执行文件(.EXE文件)

 

导入库(Import Library):

在使用动态链接库的时候,往往提供两个文件:一个引入库和一个DLL。引入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL,访问DLL中导出的函数。

 

在运行Windows程序时,它通过一个被称作“动态链接”的进程与Windows相接。一个Windows的.EXE文件拥有它使用不同动态链接库的引用,所使用的函数即在那里。当Windows程序被加载到内存中时,程序中的调用被指向DLL函数的入口,如果DLL不在内存中,系统就将其加载到内存中。

 

当链接Windows程序以产生一个可执行文件时,你必须链接由编程环境提供的专门的“导入库(importlibrary)库”。这些导入库包含了动态链接库名称和所有Windows函数调用的

 

引用信息。链接程序使用该信息在.EXE文件中构造一个表,当加载程序时,Windows使用它将调用转换为Windows函数。

 

静态库与导入库的区别:

导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了

 

地址符号表等,确保程序找到对应函数的一些基本地址信息。

 

 

静态链接与动态链接:

 

静态链接方法:#pragma comment(lib,"test.lib") ,静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来

静态库的链接可以使用静态链接,动态链接库也可以使用这种方法链接导入库

 

动态链接方法LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序

 

 

在软件开发的过程中,大家经常会或多或少的使用别人编写的或者系统提供的动态库或静态库,但是究竟是使用静态库还是动态库呢?他们的适用条件是什么呢?

 

 

简单的说,静态库和应用程序编译在一起,在任何情况下都能运行,而动态库是动态链接,顾名思义就是在应用程序启动的时候才会链接,所以,当用户的系统上没有该动态库时,应用程序就会运行失败。再看它们的特点:

 

动态库:

 

1.共享:多个应用程序可以使用同一个动态库,启动多个应用程序的时候,只需要将动态库加载到内存一次即可;

 

2.开发模块好:要求设计者对功能划分的比较好。

 

 

静态库:代码的装载速度快,执行速度也比较快,因为编译时它只会把你需要的那部分链接进去,应用程序相对比较大。但是如果多个应用程序使用的话,会被装载多次,浪费内存

 

 

二者的区别:

_exit()函数:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;

exit()函 数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。

 

exit()函数与_exit()函数最大的区别就在于 exit()函数在调用 exit 系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件

 

在Linux的标准函数库中,有一套称作“高级I/O”的函数,我们熟知printf() fopen() fread()fwrite()都在此列,它们也被称作“缓冲I/O(buffered I/O)”,其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符\n和文件结束 EOF),再将缓冲区中的内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用exit()函数。

 

在一个进程调用了exit之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构

 

 

exit()在结束调用它的进程之前,要进行如下步骤:

1.cleanup();

2.在atexit()注册的函数;

最后调用_exit()函数。。。

 

_exit()不执行I/O缓存的刷新动作

 

_exit终止调用进程,但不关闭文件(应该也关闭文件的),不清除输出缓存,也不调用出口函数。

exit函数将终止调用进程。在退出程序之前,所有文件关闭,缓冲输出内容

将刷新定义,并调用所有已刷新的“出口函数”(由atexit定义)。

 

 

_exit也会关闭文件的,要不然多来几次_exit系统不就趴下啦。

_exit做3件事(man):

1,Any  open file descriptorsbelonging to the process are closed

2,any children of the process are inherited  by process 1, init

3,the process's parent is sent a SIGCHLD signal

 

exit执行完清理工作后就调用_exit来终止进程。

 

exit0)与exit1)、return区别  

exit0:正常运行程序并退出程序;

exit1正常运行导致退出程序;

return():返回函数,若在主函数中,则会退出函数并返回一值。

详细说:

  1. return返回函数值,是关键字 exit 是一个函数

  2. return语言级别的,它表示了调用堆栈的返回;而exit系统调用级别的,它表示了一个进程的结束
  3. return
函数的退出(返回)exit进程的退出。

  4. returnC语言提供的,exit是操作系统提供的(或者函数库中给出的)。

  5. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序删除进程使用的内存空间,并将应用程序的一个状态返回给OS,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,0为非正常退出。

  6. 非主函数中调用returnexit效果很明显,但是在main函数中调用returnexit的现象就很模糊,多数情况下现象都是一致的。

 

 

i++是否原子操作?并解释为什么?

不是原子操作。理由:

 

1.i++分为三个阶段:

 

内存到寄存器

寄存器自增

写回内存

这三个阶段中间都可以被中断分离开.

 

智能指针(auto_ptr 和 shared_ptr)

1//关于一个智能指针的定义

 2template<typename Type>

 3classauto_ptr

 4{

 5public:

 6    auto_ptr(T *p =NULL) :Ptr(p)

 7    {    }

 8    ~auto_ptr()

 9    {

10       delete Ptr;

11    }

12private:

13    Type*Ptr;

14};

15

16

17void ProcessAdoption(istream &data)

18{

19

20    while(data)                            //如果还有数据

21    {

22       auto_ptr<ALA> pa(readALADara(data));

23       pa->DealProcessAdoption(data);

24    }

25    return;

26}

 

然后我看到EffectiveSTL的条款

8:永不建立auto_ptr的容器

关于此可以看的EffectiveSTL的条款8

 

因为auto_ptr并不是完美无缺的,它的确很方便,但也有缺陷,在使用时要注意避免。首先,不要将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果

 

auto_ptr的另一个缺陷是将数组作为auto_ptr的参数auto_ptr<char>  pstr (new char[12] );//数组;

为定义然后释放资源的时候不知道到底是利用delete pstr,还是 delete[] pstr;

 

然后收集了关于auto_ptr的几种注意事项:

1、auto_ptr不能共享所有权

2、auto_ptr不能指向数组

3、auto_ptr不能作为容器的成员

4、不能通过赋值操作来初始化auto_ptr

std::auto_ptr<int> p(newint(42));     //OK

std::auto_ptr<int> p = newint(42);    //ERROR

这是因为auto_ptr 的构造函数被定义为了explicit

5、不要把auto_ptr放入容器

 

static作用(作用范围,存储区域,生命周期)

1)先来介绍它的第一条也是最重要的一条:隐藏

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c

下面是a.c的内容

char a ='A'; // global variable
void msg() 
{
printf("Hello\n"); 
}

下面是main.c的内容

int main(void)

externchar a; // extern variable must be declared before use
printf("%c ", a);
(void)msg();
return0;
}

程序的运行结果是:

A Hello

你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加static,就会对其它源文件隐藏。例如在amsg的定义前加上staticmain.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。

2static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。

#include <stdio.h>

int fun(void){
staticint count =10; // 事实上此赋值语句从来没有执行过
return count--;
}

int count =1;

int main(void)

printf("global\t\tlocal static\n");
for(;count <=10; ++count)
printf("%d\t\t%d\n", count, fun()); 

return0;
}

程序的运行结果是:

global local static

1 10

2 9

3 8

4 7

5 6

6 5

7 4

8 3

9 2

10 1

3static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。不妨做个小实验验证一下。

#include <stdio.h>

int a;

int main(void)
{
int i;
staticchar str[10];

printf("integer: %d; string: (begin)%s(end)", a, str);

return0;
}

程序的运行结果如下

integer: 0; string: (begin)(end)

最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性默认值0

以上内容出自博客园Mr. Write之手,写的相当清晰易懂,存档方便复习。原文地址:http://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html

下面是中兴通讯2012校招笔试题的一道问答题:

1. static全局变量与普通的全局变量有什么区别 ?

 全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。

  全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式这两者在存储方式上并无不同。

  这两者的区别在于非静态全局变量的作用域是整个源程序当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

static全局变量只初使化一次,防止在其他文件单元中被引用;

2. static局部变量和普通局部变量有什么区别

  把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

static局部变量只被初始化一次,下一次依据上一次结果值;

3. static函数与普通函数有什么区别?

static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static修饰的函数),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.

 

对象在内存中是怎么存放的?
答:需要阅读<<Inside The C++Object Model>>
简单说来,
在类对象的内存布局中,如果有虚函数,首先是该类的vtbl指针,然后才是对象数据,对象数据都是顺序存放,当然会涉及到字节对齐,这样会带来存取效率的提升.


3. 说说COM的原理和实现?
答:COM,简单地说,是一种不同应用程序和不同语言来共享二进制代码的方法,不同于C++,只是源代码级的重用。Windows允许你使用DLL实现二进制级的代码共享,如kernel32.dlluser32.dll等,但因为这都是用C写的DLL,所以它们只能被C或者理解C调用方式的语言所调用。MFC引入了另一种二进制级的代码共享机制--MFCextension DLLs,但这种机制限制更多,你只能在MFC程序中使用它们。而COM通过建立一种二进制的规范来解决这些问题,这也意味着COM二进制模块要按照一种特别的结构来组织,在内存中亦然。规则是语言无关的,重担交给了编译器。

COM对象在内存中的组织结构和C++的虚函数一样,这就是为什么大多数COM代码都使用C++的原因,但记住,COM确实是语言无关的,因为生成的结果代码可以被其它所有语言所使用。顺便说,COM不是Win32规范,理论上,它能移植到Unix和其它任意的操作系统,但我没见过Windows世界以外的COM.


4.
为什么要用智能指针?是怎么实现的?
以免资源泄漏.内部使用了引用计数机制,具体实现非常复杂.


5. 给你一个指针,并用new动态申请空间,在另一个函数中释放,不知道是申请的一个元素还是一个数组的情况下,怎么确定用delete还是delete []?
不同的编译器有不同的实现机制,比较常用的有两种:
1.new
,在第一个对象前面记录分配了多少对象.
2.
使用键值,key-value
For ex:
int *p = new int[n];
p
key,nvalue.


6.
虚函数是怎么实现的?
答:简单说来使用了虚函数表.

7. 虚函数表又是怎样实现的?
答:
每个含有虚函数的类有一张虚函数表(vtbl),表中每一项指向一个虚函数的地址,实现上是一个函数指针的数组。

虚函数表既有继承性又有多态性。每个派生类的vtbl继承了它各个基类的vtbl,如果基类vtbl中包含某一项,则其派生类的vtbl中也将包含同样的一项,但是两项的值可能不同。如果派生类覆盖(override)了该项对应的虚函数,则派生类vtbl的该项指向重载后的虚函数,没有重载的话,则沿用基类的值。

在类对象的内存布局中,首先是该类的vtbl指针,然后才是对象数据。在通过对象指针调用一个虚函数时,编译器生成的代码将先获取对象类的vtbl指针,然后调用vtbl中对应的项。对于通过对象指针调用的情况,在编译期间无法确定指针指向的是基类对象还是派生类对象,或者是哪个派生类的对象。但是在运行期间执行到调用语句时,这一点已经确定,编译后的调用代码能够根据具体对象获取正确的vtbl,调用正确的虚函数,从而实现多态性。

分析一下这里的思想所在,问题的实质是这样,对于发出虚函数调用的这个对象指针,在编译期间缺乏更多的信息,而在运行期间具备足够的信息,但那时已不再进行绑定了,怎么在二者之间作一个过渡呢?把绑定所需的信息用一种通用的数据结构记录下来,该数据结构可以同对象指针相联系,在编译时只需要使用这个数据结构进行抽象的绑定,而在运行期间将会得到真正的绑定。这个数据结构就是vtbl。可以看到,实现用户所需的抽象和多态需要进行后绑定,而编译器又是通过抽象和多态而实现后绑定的.

 

 

0 0