第一题 C++标准中,虚表指针在类的内存结构位置没有规定,不同编译器的实现可能是不一样的。请实现一段代码,判断当前编译器把虚表指针放在类的内存结构的最前面还是最后面。

 第二题 在游戏中所有物品的实例都有一个唯一的ID,用于区分这个物品的唯一性。每个物品的ID在物品产生时就被分配了,此后将永远不会改变。每个服务器具有一个服务器ID,不同的服务器ID均不会重复,其值在0-65535之间。请设计一个生成64位的物品ID的算法,算法要求任何时刻、任何不同的服务器中生成的物品ID均不会重复。

第三题 在游戏服务端与客户端的通信中,数据的正确性是非常重要的。实际运营中发现,有一些玩家使用工具重复发送数据包,比如领取奖励的请求,发2个同样的数据包到服务器;有的玩家会篡改数据,自己随意制造一个数据包发给服务器,给服务器带来的隐患更大。为了避免这种情况,如果是你开发通信协议,如何避免重复发包,以及给逻辑数据包进行加密处理。提示:避免重复发包可以给每个数据包进行自增的编号,数据校验可以使用crc等算法产生冗余数据附加在数据头部,但是需要考虑如何做到不被轻易破解。请参考各种资料结合自己的经验编码实现;

第四题 请实现一个数学表达式计算器,计算器需要支持加、减、乘、除以及括号优先的数学运算。例如,能够对表达式“(1 + 2) * (3 / (5 - 3.2))”计算结果。

解题思路:

第一题:虚表指针在类的内存结构是在前面还是在后面,我们注意到,仅仅简单的存在成员变量和虚函数的简单类中,类的内存结构包括成员变量的空间和虚函数表指针的空间。这种顺序自然成为虚表指针和成员变量位置的先后顺序。如下图所示。①当虚表指针存在类内存结构的前面的时候,自然成员变量的位置会产生类地址的偏移量,类实例的地址和类中第一个成员变量的地址是不相等的。②当虚表指针存在类内存结构的后面的时候,类实例的地址和类中第一个成员变量的地址是相等的。因此,我们只需要定义仅有成员变量和虚函数的简单类,之后实例化类,比较类实例地址和类实例中第一个成员变量的地址,若相等,则虚表指针是放置在类内存结构后面的,否则在前面。

  1. /*
  2. 判断当前编译器把虚表指针放在类的内存结构的最前面还是最后面
  3. */
  4.  
  5. #include<iostream>
  6. using namespace std;
  7.  
  8. //测试类
  9. class CMyClass
  10. {
  11. public:
  12. int n; //成员变量
  13. //虚函数
  14. virtual void Foo(void){}
  15. virtual void Hoo(void){}
  16. };
  17.  
  18. void main()
  19. {
  20. CMyClass myClass; //定义类实例
  21. //指针类型转换
  22. char *p1 = reinterpret_cast<char*>(&myClass);
  23. char *p2 = reinterpret_cast<char*>(&myClass.n);
  24. if(p1 == p2) //若类实例地址和类第一个成员变量地址相等,则虚函数表地址放在类内存后面
  25. {
  26. cout<<"vPtr is in the end of class instance!"<<endl;
  27. }
  28. else //否则在类内存前面
  29. {
  30. cout<<"vPtr is in the head of class instance!"<<endl;
  31. }
  32.  
  33. }

第二题:在多台服务器上要生成唯一的64位的游戏物品ID,游戏服务器有唯一的serverId编号(0-65535),用16位足够保存,因此可以考虑用高16位来保存服务器id,用低48位来保存单个服务器上的服务器物品计数,提供唯一的服务器ID,再提供服务器上的递增的服务器计数值,两者合成64位的物品ID,则在多台服务器中,这个ID绝对是唯一的,可以在本地服务器就生成唯一的物品ID,从而解决多台服务器之间的数据同步问题。如下图:

  1. /*
  2. 在多台服务器上面生成唯一的游戏物品ID
  3. */
  4. #include<iostream>
  5. using namespace std;
  6. void main()
  7. {
  8. unsigned long long itemId;//游戏物品ID
  9. unsigned long long itemCount;
  10. while()
  11. {
  12. cout<<"请输入服务器ID(0-65535)"<<endl;
  13. cin>>itemId;
  14. if(itemId >= && itemId <= )//保证服务器ID输入正确(16位正整数)
  15. break;
  16. }
  17. itemId <<= ; //将服务器ID号置高16位
  18. while()
  19. {
  20. cout<<"请输入当前服务器物品个数ID(0-65535)"<<endl;
  21. cin>>itemCount;
  22. if(itemCount >= && itemCount <= 0xffffffffffff)//保证服务器物品个数不会超出(48位正整数)
  23. break;
  24. }
  25. itemId += itemCount; //服务器号和物品递增号组合
  26. cout<<"即时生成的服务器物品ID :"<<itemId<<endl;
  27. }

第四题:要实现一个简单的数学表达式的运算,其关键在于运算符优先级的问题,同时要保存优先级低的运算符和之前的运算数,及时计算优先级高的运算符的运算,且保存结果。

在此,对于运算数可以通过实现一个double运算数栈来保存需要运算的运算数,通过一个运算符栈来保存读入的运算符,将优先级低的运算符压栈,及时运算优先级高的运算。

下图是运算符的优先级表:

  1. /************************************************************************/
  2. /* 实现一个数学表达式计算器 */
  3. /************************************************************************/
  4. #include<iostream>
  5. #include<stack>
  6. #include<string>
  7. #include<stdlib.h>
  8. #include<iomanip>
  9. using namespace std;
  10.  
  11. //************************************
  12. // Method: isOperator
  13. // FullName: isOperator
  14. // Access: public
  15. // Returns: bool
  16. // 判断解析出来的字符是否是运算符
  17. // Parameter: char c 传入的运算字符
  18. //************************************
  19. bool isOperator(char c)
  20. {
  21. if(c == '+'||c == '-'||c == '*'||c == '/'||c == '('||c == ')')
  22. return true;
  23. return false;
  24. }
  25.  
  26. //************************************
  27. // Method: compareOperator
  28. // FullName: compareOperator
  29. // Access: public
  30. // Returns: char a运算符优先级高则返回'>',a运算符优先级低则返回'<',两者优先级相等则返回'='
  31. // Qualifier: 比较运算符a和运算符b的优先级
  32. // Parameter: char a
  33. // Parameter: char b
  34. //************************************
  35. char compareOperator(char a,char b)
  36. {
  37. if(a == '+'||a == '-')
  38. {
  39. if(b == '+'||b == '-')
  40. return '>';
  41. else if(b == '*'||b == '/')
  42. return '<';
  43. else if(b == ')')
  44. return '>';
  45. else if(b == '(')
  46. return '<';
  47. else
  48. return '>';
  49. }
  50. else if(a == '*'||a == '/')
  51. {
  52. if(b =='+'||b == '-')
  53. return '>';
  54. else if(b == '*'||b == '/')
  55. return '>';
  56. else if(b == ')')
  57. return '>';
  58. else if(b=='(')
  59. return '<';
  60. else
  61. return '>';
  62. }
  63. else if(a == '(')
  64. {
  65. if(b == ')')
  66. return '=';
  67. else
  68. return '<';
  69. }else if(a == ')')
  70. return '>';
  71. return '<';
  72.  
  73. }
  74.  
  75. //************************************
  76. // Method: OperatorNum
  77. // FullName: OperatorNum
  78. // Access: public
  79. // Returns: double
  80. // Qualifier: 计算数a和数b通过运算符operatorC的结果
  81. // Parameter: double a 运算数a
  82. // Parameter: double b 运算数b
  83. // Parameter: char operatorC 运算符
  84. //************************************
  85. double OperatorNum(double a,double b,char operatorC)
  86. {
  87. if(operatorC=='+')
  88. return a+b;
  89. else if(operatorC=='-')
  90. return a-b;
  91. else if(operatorC=='*')
  92. return a*b;
  93. else
  94. return a/b;
  95. }
  96.  
  97. //算数表达式计算函数
  98. //************************************
  99. // Method: myCalc
  100. // FullName: myCalc
  101. // Access: public
  102. // Returns: void
  103. // Qualifier: 计算算术表达式expressionStr的结果
  104. // Parameter: string & expressionStr 传入的算术表达式expressionStr
  105. //************************************
  106. void myCalc(string &expressionStr)
  107. {
  108. size_t i; //定义遍历字符串变量i
  109. size_t j; //定义遍历字符串变量j
  110. stack<double> NumStack; //定义操作数栈
  111. stack<char> operatorStack; //定义操作符栈
  112. operatorStack.push('#'); //初始化运算符栈
  113. for(i = ;i < expressionStr.size();i++)
  114. {
  115. if(!isOperator(expressionStr[i])) //判断是运算数的时候,解析出来运算数并压栈
  116. {
  117. string temStr="";
  118. for(j=i;j<expressionStr.size()&&!isOperator(expressionStr[j]);j++)
  119. {
  120. temStr+=expressionStr[j];
  121. }
  122. double numDouble=atof(temStr.data()); //字符串转换成double
  123. NumStack.push(numDouble); //数字压栈
  124. i=--j;
  125. }
  126. else //字符是运算符的时
  127. {
  128. char operatorC=expressionStr[i];
  129. if(compareOperator(operatorStack.top(),operatorC)=='>') //栈顶运算符优先级高过当前运算符的优先级时,需要及时运算
  130. {
  131. double a=NumStack.top();
  132. NumStack.pop();
  133. double b=NumStack.top();
  134. NumStack.pop();
  135. double c=OperatorNum(b,a,operatorStack.top());
  136. operatorStack.pop();
  137. NumStack.push(c);
  138. i--;
  139.  
  140. }
  141. else if(compareOperator(operatorStack.top(),operatorC)=='<') //栈顶运算符优先级低于当前运算符的优先级时,运算符压栈
  142. {
  143. operatorStack.push(operatorC);
  144. }
  145. else //两者优先级相等的时候,需要弹出栈顶运算符
  146. {
  147. operatorStack.pop();
  148. }
  149. }
  150. }
  151. //计算运算数栈中剩余下的结果数字
  152. for(i=;i<operatorStack.size();i++)
  153. {
  154. double a=NumStack.top();
  155. NumStack.pop();
  156. double b=NumStack.top();
  157. NumStack.pop();
  158. double c=OperatorNum(b,a,operatorStack.top());
  159. operatorStack.pop();
  160. NumStack.push(c);
  161. }
  162. cout<<"The result is:"<<NumStack.top()<<endl; //运算数的栈顶即是最终结果
  163. }
  164. void main()
  165. {
  166. string expressionStr; //定义输入算术表达式字符串
  167. while()
  168. {
  169. cout<<"Please input the new Expression"<<endl;
  170. getline(cin,expressionStr);
  171. if(expressionStr=="")
  172. break;
  173. myCalc(expressionStr); //输入且计算输出
  174. }
  175. }

三道题(关于虚表指针位置/合成64位ID/利用栈实现四则运算)的更多相关文章

  1. windows server 2003(64位)上利用iis6部署32位应用

    如果直接部署,会出现如下问题: 试图加载格式不正确的程序. (Exception from HRESULT: 0x8007000B) 解决办法 1.命令行键入: cscript.exe %SYSTEM ...

  2. 【转载】64 位 Windows 内核虚拟地址空间布局(基于 X64 CPU)

    原文链接:http://shayi1983.blog.51cto.com/4681835/1734822 本文为原创翻译,原文出处为 http://www.codemachine.com/articl ...

  3. MiniCRT 64位 linux 系统移植记录:64位gcc的几点注意

    32位未修改源码与修改版的代码下载: git clone git@github.com:youzhonghui/MiniCRT.git MiniCRT 64位 linux 系统移植记录 MiniCRT ...

  4. X86(32位)与X64(64位)有什么区别,如何选择对应的操作系统和应用程序?

    X86就是我们一般用的32位的系统,指针长度为32位(386起):X64就是64位的系统,指针长度为64位. 选择硬件对应的软件,建议通过以下三条考虑:1.64位操作系统相对32位操作系统理论上性能会 ...

  5. 64位WINDOWS系统环境下应用软件开发的兼容性问题(CPU 注册表 目录)

    应用软件开发的64 位WINDOWS 系统环境兼容性 1. 64 位CPU 硬件 目前的64位CPU分为两类:x64和IA64.x64的全称是x86-64,从名字上也可以看出来它和 x86是兼容的,原 ...

  6. <转>32位移植到64位 注意事项

    32bit-64bit porting work注意事项 64位服务器逐步普及,各条产品线对64位升级的需求也不断加大.在本文中,主要讨论向64位平台移植现有32位代码时,应注意的一些细小问题. 什么 ...

  7. Ubuntu12.4 64位 安装 arm linux gcc 4.3.2

    一.下载arm linux gcc 4.3.2 http://pan.baidu.com/share/link?shareid=1575352696&uk=2754759285&fid ...

  8. 【FFmpeg】Windows下64位ffmpeg编译

    本文主要记录在64位Windows 7下,编译64位ffmpeg的过程. 1.资源准备 (1). MSYS http://sourceforge.net/projects/mingwbuilds/fi ...

  9. C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址

    C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址 讲解之前,了解下什么是虚函数,什么是虚表指针,了解下语法,(也算复习了) 开发知识为了不码字了,找了一篇介绍比较好的,这里我扣过来了,当 ...

随机推荐

  1. 对.NET的认识

    .NET其实就是一个软件平台,这个平台和Java平台有许多的相似之处,主要表现在 1.二者编写的程序都是可以跨平台执行的   2.二者编写的程序编译后生成的都是一种中间码(IL),需要经过第二次编译才 ...

  2. [刷题codeforces]651B/651A

    651B Beautiful Paintings 651A Joysticks 点击可查看原题 651B是一个排序题,只不过多了一步去重然后记录个数.每次筛一层,直到全为0.从这个题里学到一个正确姿势 ...

  3. CESAsia:英特尔RealSense3D实感技术亮眼

    每年CES展会上都会有许多新奇的.更具创意的产品和创新技术亮相,而作为全球科技盛会之一的CES Asia也不例外.在CES Asia2016展会上,英特尔(Intel)可谓是有备而来,带着旗下支持Re ...

  4. Unix: How to Install BerkeleyDB From Source

    http://www.masaokitamura.com/2010/07/23/unix-how-to-install-berkeleydb-from-source/ This documentati ...

  5. SAP标准教材列表

    AC010 mySAP Financials Overview to Financial Accounting and ReportingAC020 mySAP Financials Investme ...

  6. Codeforces Gym 100733J Summer Wars 线段树,区间更新,区间求最大值,离散化,区间求并

    Summer WarsTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.a ...

  7. 关于MIUI悬浮窗权限问题的解决方案

    先扯会....好久没写Blog了....这段时间有点小忙...瞎忙.....忙的自己都感觉都不应该.....严重影响了生活质量......生活的幸福指数!!!.....到现在还特么的单身!!!求介绍啊 ...

  8. 详细的OS X Yosemite 10.10懒人版安装教程

    永远记住一句话:难,是因为不会.先是要放宽心态,才更利于解决安装过程中这样那样的问题.多尝试多动脑,不要有过份的依赖.很多问题到解决以后,才发现是如此的简单,我装黑苹果是拿来使用的,所以我的目的是装好 ...

  9. 图片onerror(转)

    <script type="text/javascript"> <!– function nofind(){ var img=event.srcElement; ...

  10. IOS应用程序多语言本地化解决方案

    最近要对一款游戏进行多语言本地化,在网上找了一些方案,加上自己的一点点想法整理出一套方案和大家分享! 多语言在应用程序中一般有两种做法:一.程序中提供给用户自己选择的机会:二.根据当前用户当前移动设备 ...