C++内存布局–从一个修改私有变量的问题想到的

class Test { public: int get_value() { return value; } private: int value; }; 要求不用友元,不在这个类里添加任何代码,把成员变量k的值改为100,结果自然是通过公共成员函数get_value来验证。 “不在类里添加任何代码”,除了#define private public 我实在想不出其它的“偏门”方法了。那就想想不偏门的吧,论坛里好几位朋友提供了相当于如下代码的方法(为控制篇幅,本文中所有程序段都假设已包含了<iostream>头文件并引入了std名字空间,必要时还有其它头文件): Test t; *(int*)&t = 100; cout << t.get_value() << endl; 这种方式利用对象内存布局的特点:整个类只有一个整型成员,没有继承或虚拟继承,也没有任何虚函数,那么这个对象的地址也就是它的第一个成员变量的地址,所以只需要把对象地址强转成整型,那么获得的就是那个成员变量的地址,然后对转换后的地址再解引用,修改即可,在VC2003中验证,结果是正确的。 但指针的强制转换总给人带来不爽,不大安全的感觉,上面那条最关键的语句相当于: *reinterpret_cast<int*>(&t) = 100; 也就是说,它动用了C++语言中最“强”的指针转换方式(说它最强,是因为没有什么指针之间他不能转换的)。其实我们完全可以做得更“文明”一点,方法是再定义一个联合体,比如: union TestInt { Testt; inti; }; 然后再: TestInt ti; ti.i = 100; cout << ti.t.get_value() << endl; 同样达到了目的,但实质上依据的机理跟上面的指针转换是一致的。 这个方法没啥大问题,就是有局限性,只能用于修改类的第一个成员,如果在value之前再加一个成员,比如: class Test { public: int get_value() { return value; } private: char ch; int value; }; 这种方法就不灵了。 当然,你可以手工算,认为char占一个字节,于是会试图取对象地址再加1得到成员value的地址。但第一,这种方法无法不跨平台跨实现,char及int类型在不同的平台和编译器实现中的长度都可能是不一样的;第二,没有考虑字对齐问题,在内存中,value成员一般不会紧接着排布在ch之后,而是中间间开几个字节,最后将int类型对齐到另一个位置,比如4的倍数的地址上;而更糟糕的是,字对齐不仅跟平台相关,还跟预编译指令,甚至编译选项都会有关。所以,这种手工计算的方式还是放弃了吧。 有朋友提到了使用一种宏求出value成员相对于整个对象起始地址的偏移量,即定义一个宏: #define OFFSET(TYPE,MEM) ((int)(char*)&(((TYPE*)0)->MEM)) 这个宏通过把0地址转换为TYPE指针类型,然后从这个指针上“取”MEM成员,而MEM成员的地址转换后结果就是MEM成员相对于整个对象的偏移量(我们既然是从0地址开始算的,就不用再减去起始地址0)。 然后同,我们通过使用这个宏作用于原来的类和目标字段,即: OFFSET(Test, value) 就可以获得value字段在Test类型对象中的偏移量,用对象的首地址加上这个偏移量,就可以得到value变量的地址,从而可以像上面一样解引用,修改。 这种方法不仅看起来难受,费解。事实上也根本行不通,因为这个宏所用到的技巧是从Test类型的指针上访问value成员——而valuee是private的!所以连编译都通不过。 论坛里有位朋友提出了另外一种方法可以巧妙地对付这个复杂一点的类,先做一个辅助类,它跟Test类很像,唯一的不同是它的成员都是public的: class TestTwin { public: int get_value() { return value; } public: char ch; int value; }; 于是,这个TestTwin类跟原来的Test类在内存布局上不会有什么不同,通过指针转换,很容易借助于它来修改Test类对象的value成员: Test t; TestTwin* p = reinterpret_cast<TestTwin*>(&t); p->value = 100; cout << t.get_value() << endl;… Continue reading C++内存布局–从一个修改私有变量的问题想到的

C语言测试:想成为嵌入式程序员应知道的0x10个基本问题

C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为带面试者和被面试者提供许多有用信息,此外,撇开面试的压力不谈,这种测试也是相当有趣的。  从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?这个愚蠢的问题吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗?这标志着出题者也许花时间在微机上而不上在嵌入式系统上。如果上述任何问题的答案是”是”的话,那么我知道我得认真考虑我是否应该去做这份工作。  从面试者的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C语言的水平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择,还是只是瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我发现这些信息与他们的测试成绩一样有用。  有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给正在找工作的人一点帮住。这些问题都是我这些年实际碰到的。其中有些题很难,但它们应该都能给你一点启迪。  这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。预处理器(Preprocessor) 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)  #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL  我在这想看到几件事情: ; #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等) ; 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。 ; 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 ; 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。  2 . 写一个”标准”宏MIN ,这个宏输入两个参数并返回较小的一个。 #define MIN(A,B) ((A) <= (B) ? (A) : (B)) 这个测试是为下面的目的而设的: ; 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。 ; 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。 ; 懂得在宏中小心地把参数用括号括起来 ; 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事? least = MIN(*p++, b); 3. 预处理器标识#error的目的是什么?… Continue reading C语言测试:想成为嵌入式程序员应知道的0x10个基本问题

UML Tools

以下列出一些常用的UML建模工具Apollo Eclipse 插件, Poseiden(也可以独立运行)Visual Paradigm(Crack not found)Power DesignerMagic DrawIBM Rational Architect

junit测试中的异常情况测试

需要测试一个函数抛出异常时。有两种方法写代码:public void testXXX()    {         try        {            testClass.testMethod(param1);         }        catch (Exception e)        {            if (e instanceof Exception1)            {                return;            }        }         fail();     }如上代码中,假设testClass.testMethod会抛出4种类型的异常。我们预测到传入param1给testClass.testMethod时,应该要抛出Exception1的异常。此时,如果没有抛出这个异常而成功执行的话,那么就会走到fail();。如果抛出其他非Exception1的另外3种异常时,也会走到fail();这样的结果另我们非常满意。但是事实上并不完美,原因在于假设抛出了异于我们预计的那10种异常呢?如常见的NullPointerException。此时应该要抛出error,但是这样的写法永远不会有error的情况了。似乎这样写不是最好的方案。 稍微修改一下代码如下:public void testYYYY()    {                try        {       … Continue reading junit测试中的异常情况测试

安装Office 2007时的错误

今天在安装Office 2007的时候就不断的出错。弹出的错误信息就是:Microsoft Office Enterprise 2007 encountered an error during setup。没有任何原因。于是打开了系统日志,到%TEMP%目录下找到了安装系统日志文件。找到了最后的一些日志文字说明是windows installer服务没有开启。 在此一记,希望下次不要再花那么多时间来找原因了。

0xc0150004

今天我碰到了启动MSN,QQ等都会出现0xc0150004应用程序初始化失败的问题,搜了一下,找到了解决方案。在此一记。 在C:\WINDOWS\WinSxS中搜索 “8.0.50727.163” 和 “8.0.50727.762”,然后把所有搜索到的结果都删除。

上海人啊

一个德国青年,现在在上海大学找了份交英语的工作一个女孩,他们是恋人还有这个女孩的妈妈德国青年想娶女孩女孩的妈妈不同意德国青年要加入中国国籍他说结婚后想去各处走走,五大名山,敦煌,西双版纳那个妈妈在旁边用上海话说旅游旅游你去旅游好了,你干吗不去旅游要娶我女儿然后主持人问德国青年说结婚后准备住哪儿德国青年说住女孩家这个妈妈用上海话说你凭什么住我家主持人翻成普通话问他结婚后她妈妈不让他们住他家,准备住哪儿德国青年说我们可以租房子,租两室一厅那个妈妈马上说就凭你那点钱,租什么房子,不行,一定要买房子德国青年说买房子,为什么要买房子然后说他可以打工那个妈妈马上说你打工,你去打一辈子工主持人问他你知道为什么她妈妈不同意你们结婚吗他说不太清楚,又问如果她父母不同意,你们会结婚吗德国青年说我当然会和她结婚,我是和她结婚,不是和她妈妈结婚 江苏卫视报道