网上搜刮的一些腾讯游戏客户端开发(实习)面试的考题及部分答案

真的好多都中了。。。。就先不整理自己遇到的了,依旧因为之前是在OneNote里面写的,所以排版很乱很乱

 

C/C++/C#基础

32位情况下:

Char

1个字节

short

2个字节

long

4个字节

Int, float

4个字节

Double

8个字节

long long

8个字节

"structunion的区别,他们的内存占用分别多少?

例如:

struct SA{

    int a;

    double b;

    char c;

};

 

union UA{

    int a;

    double b;

    char c;

};"

 

class A

{

};

 

问,32位系统下:

sizeof(SA) = ?

sizeof(UA) = ?

sizeof(A) = ?

 

  • 在C语言中允许用户自己指定一个组合项,在一个组合项中包含若干类型的数据项,这种数据类型称为结构体。结构体变量占用的内存大小是 各成员占的内存长度之和,每个成员分别占有其自己的内存单元 ( 不考虑边界等情况)。
  •  共用体是一种构造类型的数据结构。在一个“共用体”内可以定义多种不同的数据类型,这些变量共享同一段内存,已达到节省空间的目的,共用体内的变量互相覆盖。共用体的各成员变量在内存中的字节数可能不同,但这些变量都放在从同一个地址开始的内存单元中,共用体变量所占的内存长度等于最长的成员长度
  • 空类也要占一个字节,指针一律4个字节,虚函数有虚指针占4个字节,静态数据成员和函数不影响类大小

 

申请内存(newmalloc的区别)

  • new/delete是c++关键字,需要编译器支持

malloc/free是库函数,需要头文件支持。

 

  • 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。
  • malloc则需要显式地指出所需内存的尺寸。

 

  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。

malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

 

  • C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。

而malloc不允许重载。

 

  • new操作符从自由存储区(free store)上为对象动态分配内存空间

malloc函数从堆上动态分配内存。

 

inline的理解

  • 内联函数,提高函数执行效率(速度)

解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题

  • 内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
  • 以下情况不宜使用内联:

1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。

2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

 

const关键字用法,初始化

  • 把加了const的变量称为常量
  • C++中const的处理机制:在编译阶段把所有用到const常量的地方全部替换成常量的初始化值。
  • 必须初始化。如果不初始化无法进行替换
  • 能定义数组大小
  • 杜绝间接访问来修改常量内存的风险
  • const可以修饰成员方法,成为常成员方法
  • const可以修饰成员变量,成为常成员变量,因此他们的初始化必须写在构造函数的初始化列表当中,否者编译会报错。

 

面向对象三大特性

封装、继承、多态

  • 封装是指强调实体的本质、内在的属性。在系统开发中,抽象指的是在决定如何实现对象之前的对象的意义和行为。使用抽象可以尽可能避免过早考虑一些细节。
    • 封装:我们家里的电视机,里面有各种电路板,和电子元器件,这些电路板和元器件都被装到了电视机的壳里,相当于被封装起来了,对外提供一个开关按钮,我们一按按钮就可以打开电视

 

  • 继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。

 

  • 多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。

同一个方法对不同的对象调用行为不同的现象

 

怎么实现多态

  • 多态的实现是在基类的函数前加上 virtual 关键字使其成为虚函数,并在派生类中重写该函数;
  • 该函数运行时会根据引用或指针绑定的对象的真实类型来决定要执行的版本。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

 

多态分为四种:重载多态、强制多态、包含多态和参数多态。

重载多态分为两种:函数重载和运算符重载。

可以说,函数重载只是多态这个概念中非常小的一部分

 

虚函数

  • 关键字 virtual 只能出现在类内部的声明语句之前而不能用于类外部的函数定义。
  • 任何构造函数之外的非静态函数都可以是虚函数。

 

虚表(及其具体实现)

  • 编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。

 

虚表指针是什么

  • 虚表指针在类对象中,每个同类对象中都有个一个vptr,指向内存中的vtable,所有同类对象,共享一个vtable,但是每个对象都自带一个vptr指向这个vtable,否则调用虚函数的时候会找不到正确的函数入口,(后面将会讲明)虚表指针是对象的第一个数据成员。

 

没有继承父类的子类(因为没有父类,也可以把自己看成父类)实例化以后,通过引用或指针调用虚函数的时候,才会使用虚表指针间接访问虚函数。访问其他函数,都是静态绑定,在编译期间就已经确定下来了!

 

类的内存分布

 

STL中的map是什么(和python的字典一样)

  • Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一 种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
  • map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。

对于迭代器来说,可以修改实值,而不能修改key

 

STL中的set是什么

  • set是按照特定顺序存储唯一元素的容器。
  • 在一个set中,元素的值也标识它(值本身就是键,类型为T),并且每个值必须是惟一的。set中元素的值不能在容器中修改(元素总是常量),但是可以从容器中插入或删除它们。
  • 在内部,集合中的元素总是按照其内部比较对象(类型为Compare)指定的特定严格弱排序标准排序。
  • set容器通常比unordered_set容器通过键访问单个元素的速度慢,但是它们允许根据它们的顺序对子集进行直接迭代。

 

VectorList的区别和优缺点,什么时候用Vector,什么时候用List

  • vector (随机查找)

拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。

  • List

就是数据结构中的双向链表(根据sgi stl源代码),因此它的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。

但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。

 

  • 如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
  • 如果你需要大量的插入和删除,而不关心随即存取,则应使用list

 

类的大小

  • 一个类占用的空间主要是属性占用空间,而成员函数一般不占用空间,但是虚函数占用空间,需要说明的是,无论多少个虚函数,只要占用4个字节即可,也就是索引指向一个虚拟表的首位置。另外需要说明的是占用空间都考虑了对齐,所以不足4个的按照满4个的算。
  • 类的继承,子类占用空间是父类基础上增加本类空间即可。所以说可以认为,子类就是直接拷贝了父类的内容,然后结合自身的内容。而且存储空间也是这个顺序,即先父类分配空间,然后才是子类空间。
  • 静态成员变量不占用类空间,应该是确实没有放入这个类的里面,而且没有指针指向它,只能通过类::来访问,也就是说静态成员是随着类的存在而存在,而 不依赖于对象,它的存在意义主要还是区分,否则如何确定其意义,这还是体现了相关的都方一起的思想,比全局变量或者常量更方便使用和理解。
  • 需要说明的是,虚函数对应的虚拟表在空间的其他位置,和对象是没有联系的,但是虚拟表地址是和类统一的,也就是说一旦确定,无论在哪个对象中,其指针 值是一样的,即虚拟表位置是一定的。指针放在对象的最前面,首先是指向虚函数的虚拟表指针,然后才是其他成员变量空间

 

C++三种继承

  • 公有继承(public)

公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。

  • 私有继承(private)

私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

  • 保护继承(protected)

保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

 

  • 不管是哪种继承方式, 在派生类内部都可以访问基类的公有成员和保护成员 , 基类的私有成员存在但是在子类中不可见( 不能访问) 。
  • 使用关键字class时默认的继承方式是private, 使用struct时默认的继承方式是public。

 

虚继承 https://www.jianshu.com/p/ab96f88e5285

  • 虚继承是解决 C++ 多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:第一,浪费存储空间;第二,存在二义性问题。

 

默认构造函数

  • C++ 默认构造函数是对类中的参数提供默认值的构造函数,一般情况下,是一个没有参数值的空函数,也可以提供一些的默认值的构造函数,如果用户没有定义构造函数,那么编译器会给类提供一个默认的构造函数,但是只要用户自定义了任意一个构造函数,那么编译器就不会提供默认的构造函数,这种情况下,容易编译报错,所以正确的写法就是用户在定义构造函数的时候,也需要添加一个默认的构造函数,这样就不会造成编译报错。

 

继承得越多,类占用的空间会越大吗?

  • 类只是类型定义,没有大小可言,只有实例化后才有空间

 

右值引用为什么会提高效率?

 

C++编写多线程/进程

 

构造函数能否定义为虚函数

  • 构造函数不能是虚函数,而且不能在构造函数中调用虚函数,因为那样实际执行的是父类的对应函数,因为自己还没有构造好
  • 析构函数可以是虚函数,析构函数也可以是纯虚函数,但纯虚函数必须有定义体,因为析构函数的调用是在子类中隐含的

 

GC的工作原理

Garbage Collection

在.NET框架包含一个托管堆,所有的.NET语言在分配引用类型对象都要使用它,像值类型这样的轻量级别对象始终分配在栈中,但是所有的类实例和数组都被生成在一个内存池中,这个内存池就是托管堆

垃圾收集器的托管的基本算法很简单:

1、将所有的托管内存标记为垃圾

2、寻找正在使用的内存快,并将他们标记为有效

3、释放所有没有被使用的内存块

4、整理堆以减少碎片

 

委托

  • 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
  • Action用于没有返回值的方法(参数可以根据自己情况进行传递)
  • Func恰恰相反用于有返回值的方法(同样参数根据自己情况情况)

 

 

 

  • Delegate

 

  • Action

 

 

 

计算机网络

Tcp udp的区别

  • TCP

优点:可靠,稳定。

TCP 的可靠体现在 TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。

缺点:慢,效率低,占用系统资源高,易被攻击。

TCP 在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的 CPU、内存等硬件资源。

而且,因为 TCP 有确认机制、三次握手机制,这些也导致 TCP 容易被人利用,实现 DOS、DDOS、CC 等攻击。

 

  • UDP

优点:快,比 TCP 稍安全。

UDP 没有 TCP 的握手、确认、窗口、重传、拥塞控制等机制,UDP 是一个无状态的传输协议,所以它在传递数据时非常快。没有 TCP 的这些机制,UDP 较 TCP 被攻击者利用的漏洞就要少一些。

缺点:不可靠,不稳定。

因为 UDP 没有 TCP 那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。

 

基于上面的优缺点,那么,TCP 和 UDP 的应用场景都有哪些呢?

TCP:当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,

 

UDP:当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用 UDP。

 

TCP 与 UDP 区别总结:

  1. TCP 面向连接(如打电话要先拨号建立连接); UDP 是无连接的,即发送数据之前不需要建立连接。
  2. TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付。
  3. TCP 面向字节流,实际上是 TCP 把数据看成一连串无结构的字节流;UDP 是面向报文的。UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
  4. 每一条 TCP 连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
  5. TCP 首部开销 20 字节;UDP 的首部开销小,只有 8 个字节。
  6. TCP 的逻辑通信信道是全双工的可靠信道,UDP 则是不可靠信道。

 

游戏里面怎么判断用哪个

实时战斗游戏的话还是要用UDP了,因为TCP的特性,一旦丢包就会重发,阻塞住后续的数据包,因而可能会产生一个较大的瞬时延迟。

可以容忍延迟并且有很好的屏蔽延迟的设计,如纸牌类和MMO,用TCP

不能容忍延迟,如DOTA类和动作类,用UDP

 

三次握手

因为三次握手才能保证双方具有接收和发送的能力

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 三次握手才可以避免资源浪费

 

四次挥手

我要关闭了,你关吧,我也要关闭了,你关吧(对应FINACK

 

算法

十亿个数里面找最大的一百个:堆排序

两个人交替拿苹果,可拿1/2/3,怎么拿到最后一个

1/5/8 三种硬币怎么凑n块:dp

 

数据结构

堆和栈的区别

一个由C/C 编译的程序占用的内存分为以下几个部分:

1、栈区(stack)—> 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap) —> 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

3、全局区(静态区)(static)—> 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。

4、文字常量区 —> 常量字符串就是放在这里的。程序结束后由系统释放

5、程序代码区 —> 存放函数体的二进制代码。

 

分配方式

  • 栈有两种分配方式:静态分配和动态分配。
  • 堆是动态分配和回收内存的,没有静态分配的堆。

 

管理方式

  • 对于栈来讲,是由系统编译器自动管理,不需要程序员手动管理。
  • 对于堆来讲,释放工作由程序员手动管理,不及时回收容易产生内存泄露。

 

数据结构

  • 栈:与队列相反,栈的顺序是后进先出,只可以在栈顶进行操作,类似与只有一个出入口的公交车,先上车的只能后来下车 。
  • 堆:堆的数据机构其实就是一个完全二叉树,具堆属性的数据结构才可被叫做为堆,堆常见的应用就是堆排序与实现优先队列,因为堆的存取是随意。

 

链表和数组的优缺点

  • 数组的元素个数是固定的,而组成链表的结点个数可按需要增减;
  • 数组元素的存诸单元在数组定义时分配,链表结点的存储单元在程序执行时动态向系统申请:
  • 数组中的元素顺序关系由元素在数组中的位置(即下标)确定,链表中的结点顺序关系由结点所包含的指针来体现。
  • 对于不是固定长度的列表,用可能最大长度的数组来描述,会浪费许多内存空间。
  • 对于元素的插入、删除操作非常频繁的列表处理场合,用数组表示列表也是不适宜的。若用链表实现,会使程序结构清晰,处理的方法也较为简便。

 

深度优先和广度优先

DFS(栈),BFS(队列)

 

递归和非递归实现斐波那契数列

 

堆和栈的使用

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

 

平衡二叉树是什么

平衡二叉树又称AVL树,它的插入语、删除、查找操作均可在O(log n)的时间内完成,平衡二叉树是建立在搜索二叉树基础上的平衡。

1. AVL树或者是一棵空树,或者是具有下列性质的非空二叉搜索树:

(1) 任一结点的左、右子树均为AVL树

(2) 根结点左、右子树高度差绝对值不超过1,平衡因子定义为BF = hl -hr

 

快排原理口述

 

hash_map的复杂度和实现原理

O(1)

 

 

逻辑

如何判断点在圆里面,点在矩形里面以及点在三角形里面

圆:判断点到圆心的距离,再让距离和圆的半径比较。小于即在圆的内部,等于则再圆的的边上,大于则再圆的外部。

矩阵

 

三角形:

内部或边上:

abs( S(A,B,C) ) = abs( S(P,B,C) ) + abs( S(A,P,C) ) + abs( S(A,B,P) ) ,则P在三角形ABC的内部或边上

abs( S(P,B,C) )、abs( S(A,P,C) ) 和abs( S(A,B,P) )全都大于0,则p在三角形ABC内部,否则在边上

外部:abs( S(A,B,C) ) < abs( S(P,B,C) ) + abs( S(A,P,C) ) + abs( S(A,B,P) )

理论上不存在:abs( S(A,B,C) ) > abs( S(P,B,C) ) + abs( S(A,P,C) ) + abs( S(A,B,P) )

矩形:(叉积)BC*BP、CD* CP、DA * DP的值同号

 

操作系统

问你空类给定一个结构体, 问你在32位系统下和64位系统下它的sizeof大小.

  • 32位和64位系统在Windows下基本数据类型的大小都是一样的。
  • 只有指针的大小不一样!32位指针大小为4byte,而64位的指针大小为8byte。

 

Unity的导航

Unity 3D Navigation(导航)是用于实现动态物体自动寻路的一种技术,它将游戏场景中复杂的结构关系简化为带有一定信息的网格,并在这些网格的基础上通过一系列相应的计算来实现自动寻路。

 

怎么在导航路上设置障碍

Nav Mesh Obstacle,

再挂一个刚体组件 Rigidbody

 

游戏设计模式

单例模式、优化模式(对象池)、享元模式、命令模式、观察者模式

 

渲染管线

应用程序阶段、几何阶段、光栅化阶段

 

  • 11
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值