GEA 3.2 C/C++ 的数据、代码及内存

3.2.1 数值表达形式

3.2.1.1 数值底数

  • 人自然用十进制,计算机自然用二进制。二进制开头用0b表示。
  • 另一种常见计法是十六进制,前缀是0x,流行的原因是因为计算机分组存储数据每8位一组又称为字节。一个16进制数是4位,所以两个十六进制数可以代表一个字节。

3.2.1.2 有符号以及无符号整数

  • 符号位法
  • 补码法 更加高效并且最高位位1的时候就代表负值

3.2.1.3 定点记法

  • 第一位作为符号位 中间16位作为整数部分后面15位作为小数部分
  • 缺点在于限制了精度,解决此问题可以采用浮点记法

3.2.1.4 浮点记法

最常用的是IEEE-754标准,最高有效位是符号位中间8位为指数位决定,尾数位子则为加上小数点之前数字的值
v=s2^(e-127)(1+m)
用了相较于定点法更多的尾数位数去进行计算提高了运算精度
浮点数又不变数目的有效数字,而指数则用来把有效位推移到较高或者较低的尾数范围
在浮点数大到一定程度之后会使得数据收到一定程度上的截取
但是对于时间来说需要13天连续不断的运行游戏才会导致时间超出,所以用浮点来记时间是可行的

3.2.1.5 基本数据类型

  • char:通常是8位来存储一个ASC2和UTF-8字符
  • int、short、long:int是目标平台上最高效的运算单位 int更小而long更大
  • float:32位浮点数
  • double:双精度浮点数64位
  • bool:不同编译器不同硬件有不同大小

编译器提供了特定大小的基本数据结构的实现方法

SIMID类型

单个SIMID指令可以并行地对多个数据进行运算。
用SIMID寄存器格式打包效率更快
不同的编译器采取不同的SIMID指令进行操作

可移植的特定大小的类型

游戏引擎可以自定义自己的基本数据类型
cstdint库也提供了一些标准化的特定大小的数据类型

OGRE的基本数据类型

  • uint8、uint16、uint32无符号特定大小的整数类型
  • Real实数浮点值
  • 因为GPU总是使用32位或者16位的浮点计算,并且CPU对于单精度的计算也较快所以游戏大多采用单精度浮点型
  • u+c++基本数据类型如uchar
  • radian、degree是real的包装类,angle类是代表预设的角度单位 也就是角度还是弧度
  • OGRE没有定义带符号的整数类型可能需要自己去定义

3.2.1.6 多字节值及字节序

大于一个字节的值称为多字节量。
在内存中存储多字节整数有两种方式,不同的微处理器选择不同

  • 小端:最低有效字节存储在较低的内存位置,该处理器就是小端处理器。
  • 大端:与小端处理器相反,将最高有效字节存储于较低的内存位置
    字节序:字节的排序

PC是小端而游戏主机预设是大端
解决办法:

  • 将数据用文字方式写入文件,浪费磁盘空间但是却可行。
  • 先用工具转换数据字节序.

整数字节序转换 交换最高字节和最低字节,难点在于要知道那些字节序需要转换
需要知道struct里面每个数据成员的大小以及位置
<< 是左移运算符的意思
“>>” 右移运算符
单个&和|是位与和或
浮点字节序转换 通过reinterpret_cast操作把浮点数转化为整数但是可能导致优化bug,这时候应该采用union的方法解决这个问题

3.2.2 声明、定义及链接规范

3.2.2.1 再谈翻译单元

编译器翻译.cpp文件输出.o或者.obj
输出的对象文件可能包含未解决的引用(来自其他.cpp文件的)
链接器的任务就是把所有对象文件组合成为最终可执行镜像,解决外部引用是它的主要作用
EXTERN关键字: extern是一个关键字,它告诉编译器存在着一个变量或者一个函数,如果在当前编译语句的前面中没有找到相应的变量或者函数,也会在当前文件的后面或者其它文件中定义

  • 找不到extern引用的目标会报错
  • 找到多个extern引用的目标也会报错因为过多

3.2.2.2 声明与定义

  • 声明:数据对象或者函数的描述。使得编译器知道其一些属性。
  • 定义: 程序中个别内存区域的描述用来放置变量、struct或者class的实例

定义是实体的本身而声明是实体的引用。
函数定义:置于函数签名之后的大括号
函数声明:在函数变量后直接加上分号;也可以在前面有选择的加上extern
加上extern后可以用extern关键字做声明
多重声明和定义 可以进行多重声明但是不能进行多重定义,但是如果进行了多重定义不进行交叉引用的检查是无法检查出来的
头文件中的定义以及内联把定义放在头文件中然后去include是非常危险的可能会导致多重定义问题。

内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。必须得看得见函数主体才可以内联。内联只是给编译器的批注,最终如果雀氏性能得到了优化才会进行内联。

3.2.2.3 链接规范

  • 内部链接智能被该定义所处的翻译单元所看见
  • 外部链接可以被定义处以外的翻译单元看见

使用static可以把定义改为内部链接,两个不同的cpp文件中分别定义两个同名文件并且用static则认为是不同的实体

3.2.3 C/C++ 内存布局

3.2.3.1 可执行映像

类unix系统的可执行文件格式为.ELF
而windows采用的格式为.exe
程序执行时除了会把该可执行映像置于内存中还会分配更多的额外内存
可执行映像被分为几个相连的块,这些块被称为段segment或节section
映像文件一般最少由以下4个段组成

  • 代码段 全部函数的可执行机器码
  • 数据段 全部获初始化的全局以及静态变量。分配和运行时一样的内存。
  • BSS段 包含程序未初始化的全局和静态变量并且包含了所需0的字节数量
  • 只读数据段 const 注意明示的整数常是作为代码段而不是只读数据段

3.2.3.2 程序堆栈

堆和栈的区别:
1、申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;
2、申请大小的不同。栈获得的空间较小,而堆获得的空间较大;
3、申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;
4、存储内容的不同。栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;
5、底层不同。栈是连续的空间,而堆是不连续的空间。

当可执行程序被载入内存的时候会保留一块称为程序堆栈的内存。
a调用b b被压在a之上 返回值的时候b pop
堆栈帧存储3类数据:

  1. 堆栈帧存储调用函数的返回地址。可以凭借这个地址继续执行调用方的函数。
  2. 堆栈帧保存相关CPU寄存器的内容。被调用方可以使用任何合适的寄存器
  3. 包含函数里的所有局部变量。
    一般通过调整一个CPU寄存器的值 即堆栈指针来实现堆栈帧的压入和弹出
    局部变量只是暂时存储而已 调用下一个函数时就会被覆盖
    不可以返回局部变量的地址,会导致错误

3.2.3.3. 动态分配的堆

全局变量和静态变量存储于可执行映像中,局部变量存储于堆栈帧中。
对于不能在编译时完全知道所需内存的时候程序就需要动态分配内存。
可以手动使用malloc或者windows中的heapalloc分配再通过free释放,这块内存被称为堆内存即自由内存。
全局new和delete操作符可以自由存储和释放内存。但是如果被重载了new和delete就不是必然从堆中分配内存了。

3.2.4 成员变量

class和sruct的声明并不占内存,因为只是数据布局的描述。声明了一个class或者struct时候就可以通过基本数据类型相同的方法来调用这些class或者struct。

3.2.4.1 类的静态成员

static有许多不同的含义

  1. 用于文件作用域时,意味着限制变量或者函数的可见性,只有这个cpp才能使用变量和函数
  2. 作用于函数作用域的时候意味变量为全局变量,只在本函数可见
  3. 作用于struct或者class的时候意味此变量类似于全局变量
    作用于class时候不会修改该变量的可见性,而是通过public、private、protected决定
    类似于extern定义的全局变量其本身并补分配内存而在一个文件中定义的时候才分配内存。
    请添加图片描述

3.2.5 对象的内存布局

class和struct中区分每个基本数据类型的所占的bit大小
8个bit是一个byte

3.2.5.1 对齐和包裹

内存控制器会对没有对齐的格式的位进行掩码和移位十分麻烦
CPU以32位4个字节位对齐格式
应该在定义数据结构时候优先把大的放前面把小的放后面来提高对齐和包裹效率
并且会多出明确的一些填充字节数在末尾
类似于俄罗斯方块?拼图?

3.2.5.2 C++类的内存布局

当一个子类继承自父类时他会将它的数据结构定义在父类数据结构所占的内存空间之后并且会填充对齐
(避免多重继承)
对于虚函数的继承会添加一个虚表指针指向虚函数表,而每一个虚函数都有一个表 实现多态

3.2.6 kilobyte和kibibyte

kibi是1024 kilo是1000

猜你喜欢

转载自blog.csdn.net/TongOuO/article/details/122641555
今日推荐