| Profil de Duo一心天堂PhotosBlogListes | Aide |
|
|
29 octobre windbg+vmware单机调试内核windbg内核调试 windbg进行内核调试,一般要双机,很多时候是用来调试驱动,或是操作系统内核的。但是最近查了很多网上资料,本机内核调试应用程序的dmp文件的话,能提供非常多的有用信息。双机调试环境毕竟有点困难,要么用windbg+vware。但windbg非常奇怪,切换到kenerl mode后不能attach process也算了(这是user mode)。但居然连dmp都不让打开。所以如果用windbg进行本机内核调试,要使用.opendump命令,手动加载dmp文件。 2 novembre 成员函数指针这几天因为设计上得需要,突然想起用成员函数指针,老实说,基本上没有用过这个,以前都是用函数指针来做回调函数得,所以第一次使用发生了许多问题。 具体测试代码如下: #include <iostream> using namespace std; class TestA struct C { typedef void (TestA::*MEMBERFUNC)(); class TestB void testB() TestA* ptr_a; }; int main() b.ptr_a = &a; 我原本意图将B类得成员函数指针中保存指向A类得成员函数,但是编译老是出错。查看了MSDN相关得内容和网上一些关于使用成员函数指针得文档后,改成如上得样子。 具体得问题在于成员函数指针必须动态得绑定到具体类实例 因此在B类中声明A类成员函数得成员函数指针变量时,形式如typedef void (A::*func)(); 这样得声明在,调用时,因为B类中会传默认得this指针,就成为了B::func,所以无法调用成功,必须在->操作符前指明指向A得指针,我因此加了ptr_a; 但这样一来,我觉得不如直接在B类中保存A类指针,那还需要用成员函数指针吗,白白增加了复杂度 通过几个MSDN例子,发现成员函数指针多用在指向自己本身得成员函数 这样看来,只有在同类,不同实例互相调用时使用会比较方便了,否则貌似没必要,大概这也是为什么它得使用不及函数指针吧 27 juin 关于基于帧的内存分配方式在研究了目前项目使用的内存分配方式和HAVOK的内存分配方式后,发现: 我们的项目中使用的并非是基于帧的内存分配方式,而是一种很接近于帧的内存分配的方式。和大多数的游戏一样,我们使用自己的内存分配和释放函数(通过对new和delete的重载)。这里先要谈论的是在游戏中的内存管理 游戏中之所以不采用系统标准的内存管理函数,其原因是从效率上考虑的。而采用系统标准的内存管理函数的话,第一是容易产生内存碎片,第二无法添加对内存进行监控和调试的信息,第三,无法将物理内存映射为连续的逻辑内存。 为了解决以上问题(参考Game Gem1中的主要矛盾为内存碎片),发明了基于帧的内存分配方式。“帧”在这里的概念我个人理解为一个节点,而非通常意义上的视频概念里的一帧,也不是Game Gem中解释的句柄(句柄这东西实在不好理解,老手当然懂了,我觉得还是解释为节点一目了然)。所谓的“节点”就是保存对于一个特定时间点上内存操作的信息。比如什么时候分配了内存,分配了多少,什么时候释放了内存等 在基于帧的内存分配里,帧就是保存内存分配时,申请到的内存地址。比如你执行一次new,返回一个0xADD1的地址,在执行一次,返回0xADD2,那么这是我们有一个struct来保存这两个指针,也就是形成了两个帧 基于帧的内存分配的思想是,事先划分一块较大的内存供日后申请,每次申请保存帧信息。并且内存分配采用类似堆栈的方式,也就是先进后出(采用堆栈,我认为是由内存工作方式,和汇编指令工作方式造成的,先占领地位内存地址) 在保存完帧信息后,无论我们对接下来的地址如果做分配,释放。只要在回收该块内存后,使用帧信息就不会产生碎片,比如:帧中记录内存地址为0x00001111,然后对起始地址后的4K空间进行分配,第一次分配结构structA的数组,大小为24B,第二次分配结构structB的数组,数组元素大小37B,这样类似大小不一就容易产生碎片,但如果我们始终按照0x00001111的地址释放其后面的空间,就可以把碎片回收,同时反复使用了我们预先分配的内存 目前,我们的项目中是采用链表方式来完成这一功能,虽然也做到了反复利用预先申请的内存,也使用链表的后向指针来保存了类似帧的信息,但是我们采用判断申请内存是否大于现有Free块,才决定是否分配,所以如果申请内存总是比free块小一点,是会产生碎片的 在HAVOK中的基于堆栈的内存分配,在释放时,将一次性释放其后所有内存地址。同时在一些havok中的步进运算中,确实可以采用先进后出的方式来抛弃指定帧后的无用数据,加快速度和内存使用效率。 即使,在局部方法中,由系统分配堆栈,但是可能UNIX和WINDOW等操作系统将消耗额外CPU时间在逻辑上组合一块连续内存,而对于一些游戏机上无此类高级内存管理功能的情况,有可能就无法请求比较大的内存块。因为我们构建预先使用的一块堆栈用内存,并提供申请释放方式。当在局部方法中使用时,来代替系统分配的堆栈,并在方法结束时释放堆栈,就解决了以上问题。 6 mars 关于宏的一个发现 根据代码大全,宏应该采用#define macro() ()的格式,这样不容易出错
但是,我发现如果将一段内联函数,也就是一段语句使用宏来代替,这个格式会引发错误
#include <cstdlib>
#include <iostream> using namespace std;
#define test(a) a = a + a*(a);\ ======》》这里不能加括号,只能用\来连接语句,否则会报错
a++; int main(int argc, char *argv[]) { int a = 0; test(a); system("PAUSE"); return EXIT_SUCCESS; } 23 novembre 字节对齐 关于字节对齐,在编译器中可以使用的是/zp开关,或是使用#pragma编译器指令。/zpn,n代表自然数,和#pragma pack(n)的作用一样,支持1,2,4,8,16字节对齐。
字节对齐的意思就是让数据存储在自然边界上。所谓自然边界就是指,如果是对字来说,处于偶数地址,对于双字就是能被4整除的地址,以此类推。字节对齐的好处可以让访问数据时一次性读入数据。比如,如果8字节数据,处于自然边界,并且使用8字节对齐,那么只需访问一次内存就能取得8字节数据,如果这8个字节没有处于自然边界,比如从0x00000003上开始,那么要存储到0x0000000B,但读内存的时候,需要先访问0x00000001到0x00000008,在读取0x00000009到0x0000000B,两次才能读取到8字节数据,效率低。
字节对齐的原则我概括为以下几条
1.第一,字节对齐是以设定的n最为最大内存宽度,并不是一次必须分配n空间来保证对齐,即对齐不是一定要你的倍数存储地址或是空间
2.第二,如果,你要保存的数据,超过n或是n不足,那么以小的数据存储,如果剩余的空间还能存放下一个成员数据,那么就存放,如果不能存放,则要等下一个8字节空间。剩下的空间能不能存放,要根据变量的自然边界。比如先存放了5字节数据,8字节对齐情况下,long型自然边界为4字节(long的长度为4),所以要存放在下一个8字节空间。
注:这句话在MSDN翻译的很拗口,我以自己的理解翻译了下
struct a
{
long s;
char s1[5];
}
这个结构在8字节对齐需要的存储空间为12字节,long s占用4字节(自然边界对齐),s1占8字节,因为5个char不能放在前一个8字节的4字节空间中,分配了下一个8字节空间
struct b
{
long s;
char s1[3];
long s3;
char s4
}
为16字节 14 novembre 关于虚函数的访问权限 对虚函数已经习惯了使用public权限访问了,今天整理了一下,虚函数其实是可以用private进行修饰。其规则和用途是这样的
1.构造函数不允许使用虚函数
2.析构函数可以使用虚函数,甚至说必须使用虚函数,当你从基类派生时
3.虚函数可以使用private进行访问权限修饰,但派生类不能调用基类的虚函数,它只能使用自己的虚函数。这里其实和用普通函数是一样的。即不能在外部访问私有成员函数。只能在类使用私有成员函数。当在类中访问一个虚函数时。由于传递的隐藏参数this指向的是自己,所以会调用自己的虚函数。同样,当通过基类指针访问一个派生类对象的虚函数时,传递的this指针,实际是指向派生类对象的,所以调用的还是派生类虚函数。也就是说,虚函数的访问修饰符,起的作用只是在外部调用时。它的意思是,我是虚函数,我可以被继承形成多态,但是只有我的同类知道怎样,并且可以使用我。外部人员无法使用我的多态性。这也就解释析构函数在类继承中必须是虚的,并且public,否则如下,delete c时,如果非虚析构,A就没有释放,如果是private,就无法调用~A(),编译报错。
4.相对的,构造函数只允许内联
class A
{ public: A() {printf("虚构造函数被调用\n");} virtual ~A() {printf("虚析构函数被调用\n");} void aa() {test();}
private: virtual void test() {printf("test虚函数,私有在A类中被调用\n");} }; class B : public A
{ public: B() {printf("B类虚构造函数被调用\n");} virtual ~B() {printf("B类虚析构函数被调用\n");} void bb() {test();}
private: virtual void test() {printf("test虚函数,在私有B类中被调用\n");} }; int _tmain(int argc, _TCHAR* argv[])
{ A a; B b; A* c = new B; a.aa(); b.bb(); b.aa(); c->aa(); delete c; system("PAUSE"); return 0; } 不过,今天看MSDN的官网说,2005的新语法不支持访问私有虚函数,但托管C++却是可以的,因为没有VS2005环境,所以我也没有验证,有空去试试。
今天先到这,要上班了。晚上继续程序的分析
|
|
|