精华内容
下载资源
问答
  • 数组在内存中存放数据根本

    千次阅读 2018-04-22 18:34:32
    功能完善好了之后,最后突然发现了一个很蛋疼bug,就是这里看着明明是一个坑,为什么马里奥就是掉不下去呢,并且更奇葩是在第一关中马里奥就可以很正常掉到坑里面,但是为什么在第二关就是掉不下去了呢,...

    花费了近三个星期写了一个超级玛丽的游戏,功能完善好了之后,最后突然发现了一个很蛋疼的bug,就是这里看着明明是一个坑,为什么马里奥就是掉不下去呢,并且更奇葩的是在第一关中马里奥就可以很正常的掉到坑里面,但是为什么在第二关就是掉不下去了呢,检查地图数组数据的初始化时没有任何问题的,第一关和第二关只是用的贴图不同罢了,代码逻辑都是同一个代码,这究竟是为什么呢?这个问题真是有些奇葩了


    地图数组定义

    struct Unit
    {
    	short row;		//行
    	short col;		//列
    	short id;		//id号
    	short addInfo;	//附加信息(表示是宽度还是高度)
    	short page;		//页数	
    	POINT marioPos;										//玛丽进入管道或者出来管道应在的位置
    	//short marioPosX;
    	//short marioPosY;
    };
    typedef Unit UNIT[13][16];   //地图中的每页的二维数组
    	UNIT* mapArr;			//将地图信息全部保存到MapArr三维数组中去

    经过调试发现为什么判断玛丽是否可以下降的代码中,竟然玛丽奥的下面位置的id号不为0,而是26(26就是表示是青砖的位图id号),并且我定义的地图的行一共才13行,所以下标应该是只能到12,下标到了13行了,为什么没有越界呢,后来经过将问题进行简单化做了一个例子,才发现虽然我定义的地图每页是13行16列的二维数组,但是前面还有页数呢,所以即使是行也是不会越界的

    例如下面的程序:

    #include "stdafx.h"
    typedef int INT[3];
    int main()
    {
    	INT* pInt = new INT[3];
    	memset(pInt, 0, sizeof(INT) * 3);
    	pInt[2][1] = 10;			
    	//2行1列为10     实际上也可以写成0行 7列或者1行4列都是一样的(我猜想的,下面进行验证)
    	printf("pInt[2][1]=%d\n", pInt[2][1]);
    	printf("pInt[0][7]=%d\n", pInt[0][7]);
    	printf("pInt[1][4]=%d\n", pInt[1][4]);
    	/*
    		验证结果得出三个答案都是10
    		原因就是因为在内存中数据没有一维二维之分,只是一位一位的内存序列号
    	*/
    	printf("%d\n", pInt[9][10]);         //这个就是正真的越界了
    	while (1) {}
        return 0;
    }

    所以在我程序中虽然mapArr[5][13][0]   表面上看13行这一行是越界了,但是实际上并没有,实际上这个位置的数据等于mapArr[6][0][0]的数据是一样的,又因为在初始化的时候mapArr[5][0][0]这个在数组内存中的值为26,不为0,所以在判断马里奥是否能够下降代码中就会判断出来不能下降了,所以就会看到马里奥虽然是在坑里面,但是就是不往下掉落,一开始说的第一关中的所有坑都可以掉落,原因也正是如此,这个不能掉落的坑只是一个巧合罢了,实际上所有的坑因为这个原因都会出现有的坑能够掉落,有的坑不能掉落的情况


    原因既然找到了,那么bug就很容易改了嘛,如果判断出来马里奥的脚的下面的一个像素的位置所在的行是大于定义的行的,就判断为马里奥能够下降,这样马里奥就能够掉落到所有的坑里面了,然后在其他地方进行判断如果马里奥全部掉出了地图,那么就证明马里奥是死亡了

    判断玛丽能否下落的代码

    bool Mario::IsCanDown(Map &map, vector<Enemy> &enemyVect,vector<Ladder*> &ladderVect)
    {
    	CheckEnemyColider(enemyVect);
    	if (CheckLadderColider(ladderVect,false))
    	{
    		return false;
    	}
    	Unit tempLeft, tempRight;
    	//tempLeft.page = posX / (unitW*16);
    	tempLeft.page = (posX + 2) / (unitW * 16);          //上面的写法是不正确的,既然列进行了posX偏移,那么求page的PosX也应该进行偏移(如果posX=4479,那么用posX求的page=7但是用posX+2求的page就是8,就会出现得到的列数不是同一页的列)
    	tempLeft.row = (posY + height*coliderSize - 1 + 1) / unitH;
    	//之所以要+2,这样做即使人物的左边角是在障碍物右边两个像素的位置上方,这样人物依然可以下降的
    	//不然如果一个空格两边都是平的砖块,那么人物必须要正正好好站在空格地方之上,一个像素也不能差,这个位置太难找了
    	//会出现看着人物下面是有一个空格子,但是人物下不去的情况,为了解决这个问题,才进行了+2以及-2操作,允许有两个像素位置的偏差
    //	tempLeft.col = (posX % (unitW*16)+2) / unitW;                 
    	tempLeft.col = (posX + 2) % (unitW * 16) / unitW;          //应该是这样写的,上面的写法是不正确的
    	tempRight.page = (posX + width - 1-2) / (unitW*16);
    	tempRight.row = tempLeft.row;
    //	tempRight.col = ((posX + width - 1) % (unitW*16)-2) / unitW;
    	tempRight.col = (posX - 2 + width - 1) % (unitW * 16) / unitW;			//应该这样写,先偏移在求余
    	UNIT* mapHead = map.GetMapArrHead();
    	if (tempLeft.row >12)    //玛丽的脚如果是大于定义的地图的高度12行之后不管下面是什么都直接返回true,证明能够继续掉落
    	{
    		return true;
    	}
    	if (mapHead[tempLeft.page][tempLeft.row][tempLeft.col].id != 0 || mapHead[tempRight.page][tempRight.row][tempRight.col].id != 0)
    	{
    		return false;       //表示下面为障碍物,不能继续降落了
    	}
    	return true;       
    }




    展开全文
  • 它们所需要空间大小可以明确计算出来,并且不会再改变,因此它们可以直接存放在可执行文件特定节里(而且包含初始化值),程序运行时也是直接将这个节加载到特定,不必在程序运行期间用额外代码来...

    还没有运行怎么会占用内存呢?!(这一点还要怀疑吗!?)

    所谓在编译期间分配空间指的是静态分配空间(相对于用new动态申请空间),如全局变量或静态变量(包括一些复杂类型的常量),它们所需要的空间大小可以明确计算出来,并且不会再改变,因此它们可以直接存放在可执行文件的特定的节里(而且包含初始化的值),程序运行时也是直接将这个节加载到特定的段中,不必在程序运行期间用额外的代码来产生这些变量。
     
    其实在运行期间再看“变量”这个概念就不再具备编译期间那么多的属性了(诸如名称,类型,作用域,生存期等等),对应的只是一块内存(只有首址和大小),所以在运行期间动态申请的空间,是需要额外的代码维护,以确保不同变量不会混用内存。比如写new表示有一块内存已经被占用了,其它变量就不能再用它了; 写delete表示这块内存自由了,可以被其它变量使用了。(通常我们都是通过变量来使用内存的,就编码而言变量是给内存块起了个名字,用以区分彼此)

    内存申请和释放时机很重要,过早会丢失数据,过迟会耗费内存。特定情况下编译器可以帮我们完成这项复杂的工作(增加额外的代码维护内存空间,实现申请和释放)。从这个意义上讲,局部自动变量也是由编译器负责分配空间的。进一步讲,内存管理用到了我们常常挂在嘴边的堆和栈这两种数据结构。

    最后对于“编译器分配空间”这种不严谨的说法,你可以理解成编译期间它为你规划好了这些变量的内存使用方案,这个方案写到可执行文件里面了(该文件中包含若干并非出自你大脑衍生的代码),直到程序运行时才真正拿出来执行!

    展开全文
  • 还没有运行怎么会占用内存呢?...如全局变量或静态变量(包括一些复杂类型常量),它们所需要空间大 小可以明确计算出来,并且不会再改变,因此它们可以直接存放在可执行文件特定节里(而且包含初始化...

    还没有运行怎么会占用内存呢?!(这一点还要怀疑吗!?)

    所谓在编译期间分配空间指的是静态分配空间(相对于用new动态申请空间),如全局变量或静态变量(包括一些复杂类型的常量),它们所需要的空间大 小可以明确计算出来,并且不会再改变,因此它们可以直接存放在可执行文件的特定的节里(而且包含初始化的值),程序运行时也是直接将这个节加载到特定的段 中,不必在程序运行期间用额外的代码来产生这些变量。
     
    其实在运行期间再看“变量”这个概念就不再具备编译期间那么多的属性了(诸如名称,类型,作用域,生存期等等),对应的只是一块内存(只有首址和大小), 所以在运行期间动态申请的空间,是需要额外的代码维护,以确保不同变量不会混用内存。比如写new表示有一块内存已经被占用了,其它变量就不能再用它了; 写delete表示这块内存自由了,可以被其它变量使用了。(通常我们都是通过变量来使用内存的,就编码而言变量是给内存块起了个名字,用以区分彼此)

    内存申请和释放时机很重要,过早会丢失数据,过迟会耗费内存。特定情况下编译器可以帮我们完成这项复杂的工作(增加额外的代码维护内存空间,实现申 请和释放)。从这个意义上讲,局部自动变量也是由编译器负责分配空间的。进一步讲,内存管理用到了我们常常挂在嘴边的堆和栈这两种数据结构。

    最后对于“编译器分配空间”这种不严谨的说法,你可以理解成编译期间它为你规划好了这些变量的内存使用方案,这个方案写到可执行文件里面了(该文件中包含若干并非出自你大脑衍生的代码),直到程序运行时才真正拿出来执行!

    展开全文
  • 进程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么的。 ...

    进程(执行的程序)会占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途 不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。对任何一个普通进程来讲,它都会涉及到5种不同的数据段。
    Linux进程的五个段:

    下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的。

    BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

    数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

    代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

    堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

    栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    它是由操作系统分配的,内存的申请与回收都由OS管理。

    PS

    全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

    bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。
    data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。 数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。


    总结

    1、栈空间

    1)动态局部变量
    2)形参
    3)函数返回地址

    2、堆空间

    1)malloc/new所申请的内存空间

    3、数据段(静态存储区)

    1)静态变量
    2)全局变量
    3)字符串常量

    4、代码段

    程序代码主体、函数主体、为二进制形式
    在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等

     

    展开全文
  • c/c++ 的内存机制

    2013-10-13 15:09:00
    进 程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么
  • 进 程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么
  • 内存对齐

    2020-09-23 22:51:39
    默认的对齐方式:各成员变量在存放的时候根据在结构出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节编译器会自动填充。 同时编译器为了确保结构的大小为结构的字节边界数(即该结构占用...
  • 现代计算机中内存空间都是按照byte划分,从理论上讲似乎对任何类型变量访问可以从任何地址开始,但实际情况是在访问特定类型变量时候经常在特定的内存地址访问,这就需要各种类型数据按照一定规则在空间上...
  • 进 程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么
  • 以下文章来自:https://www.jianshu.com/p/8a89fb6d839c,这篇文章涉及多方面知识,所以...堆是存储时单位,对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大一块。线程共享,主要是存放对象实例和数组。
  • C++ 内存对齐

    2020-05-29 00:40:29
    什么是对齐 : 现代计算机中内存空间是按照byte划分,对类型访问不能从任意位置开始, 访问特定变量要在特定内存地址访问,需要各类型数据按照一定规则在空间上排列,而不是顺序一个接一个排放,这就是对齐。...
  • 而不同平台的CPU对内存读取的要求都不同,有些平台只支持在特殊的位置读取特殊的变量,一旦内存没有对齐,可能会直接抛出硬件异常,于是编译器会对结构体数据在内存中存放的位置按一定的规则进行排布,以适应特殊...
  • 原文链接:wukong.com/answer/6820743715972186375/项目本身不需要使用缓存缓存是为了将热点数据存放内存中,减少应用系统与数据库交互次数以及传输,从而...
  • 项目本身不需要使用缓存缓存是为了将热点数据存放内存中,减少应用系统与数据库交互次数以及传输,从而提高系统响应时间。在高并发系统中,缓存技术是应用非常普遍技术,因为系统最终瓶颈都会落到数据库...
  • 垃圾收集器与内存分配策略 一、GC概述 ​ GC要做三件事: ...​ 在堆存放了大量对象实例,有些对象不可能再被使用了,就已经死了,需要被回收。所以如何判断对象已经死了呢,就有下面几种方法。 1. 引...
  • 有些函数,如strtok,strrep,只能使用①,为什么? Str1和str2区别在哪里?先说结论! Str1值”hello”存储在可读可写区,而str2值”world”存储在只读区。 某些函数会修改字符串,如果存放在只读区...
  • Linux中的五个数据段

    千次阅读 2017-09-18 11:31:12
    进程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么的。BSS段:BSS段(bss s
  • 在使用动态分配应用程序,有时会有这样情况出现:程序试图读写一块“应该可用”的内存,但不知为什么,这个预料可用光标已经失效了。有可能是 “忘记了”向操作系统要求分配,也可能是程序自己在某个时候...
  • 现代计算机中内存空间都是按照byte划分,从理论上讲似乎对任何类型变量访问可以从任何地址开始,但实际情况是在访问特定类型变量时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定规则在空间...
  • 进 程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么
  • 对ubootBSS段理解

    千次阅读 2013-03-31 08:56:53
    而BSS段中存放的是一些未初始化的静态变量、全局变量,为了合理的使用内存,那么在最后编译出来的.bin文件中BSS段是不存在的。针对这个我自己给自己提出问题,以下答案依据个人理解,可能我描述有些问题,也可能我...
  • 什么是对齐,以及为什么要对齐:现代计算机中内存空间都是按照byte划分,从理论上讲似乎对任何类型变量访问可以从任何地址开始,但实际情况是在访问特定变量时候经常在特定的内存地址访问,这就需要各类型...
  •  物理存储结构:在机器内存中的存放  数据对象必定与一系列加在其上操作相关联  实现这操作工具就是“算法”  抽象数据类型:  不关心数据操作实现,数据类型定义。只建立一个大体框架 2.什么是...
  • - 对象是内存中专门用来存储数据一块区域。 - 对象中可以存放各种数据(比如:数字、布尔值、代码) - 对象由三部分组成: 1.对象标识(id) 2.对象类型(type) 3.对象值(value) 二、面向对象和面向...
  • 进程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入...Linux进程五个段下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么的。BSS段:...
  • 进 程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么的。...
  • 进程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么的,Linux进程五个段: BS...
  • 进程(执行程序)会占用一定数量的内存,它或是用来存放从磁盘载入程序代码,或是存放取自用户输入数据等等。...下面我们来简单归纳一下进程对应的内存空间所包含5种不同数据区都是干什么的。 ...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 123
精华内容 49
关键字:

内存中存放的有些什么