C++咬文嚼字-'Pointer Trick'

晚上饭后抽空看了看如何实现一个内存管理器,涉及内存操作必定少不了指针,恰研究到offsetof这个operator,也看了它的实现,顿有所悟。

先看一段代码:
struct Foo {
        int     a;
        char*   p;
        char    b;
};

Foo* p1 = reinterpret_cast(0);
Foo* p2 = 0;

bool b = (p1 == p2);
std::cout << b << std::endl;

输出结果:
1

请考虑一下虽然打印出来的结果是:1,但是这两个赋值语句的意义相同吗???

Foo* p2 = 0;
相信所有C++的初学者都知道,这句的意思是p2是一个空指针,或者说p2尚未指向任何对象。

Foo* p1 = reinterpret_cast(0);
那么这句呢?难道p1也是如p2一样,是一个空指针么?

我们再回顾一下一般我们给指针赋值都是如何做的:
Foo* p = new Foo();

Foo aFoo;
Foo *p = &aFoo;

无论是new Foo()还是&aFoo,实际上他们返回的都是一个地址常量,类似于0xffbff204这样的地址值,我们就不妨假设new Foo()或&aFoo返回的地址常量值就为0xffbff204。

继续!我们将如何解释一个指针呢?在Stanley B. Lippman的鸿篇巨制'C++ Primer 3rd'中的第3.3小节有这样的叙述:
每个指针都有一个相关的类型。… … 指针的类型可以指示编译器怎样解释特定地址上内存的内容以及该内存区域应该跨越多少内存单元。

好了,我们解释一下Foo *p = 0xffbff204; (或Foo *p = (Foo*)0xffbff204 或Foo *p = reinterpret_cast(0xffbff204) 更好一些,因为Foo *p = 0xffbff204;这样的语句在C++中很可能不能通过编译,并提示'invalid conversion from `unsigned int' to `Foo*'之类的错误')
我们有这样一个Foo类型的指针,其指向一个起始地址为0xffbff204的类型为Foo的对象,这样我们可以通过&(p->b)得到b的地址:即在0xffbff204的基础上再加上成员b在结构体中的偏移量,如偏移量是8,我们得到的b的地址就是0xffbff204 + 8d。

现在我们把0xffbff204换成了0,也就是Foo* p1 = reinterpret_cast(0); 其实我们再告诉编译器:我们有这样一个Foo类型的指针,其指向一个起始地址为0×0的类型为Foo的对象。这时如果我们想得到b的地址,我们一样可以通过&(p->b)获得,即在0×0的基础上再加上成员b在结构体中的偏移量,如偏移量是8,我们得到的b的地址就是0×0+ 8d = 8,其实这就是b在结构体里面的偏移量。

有些人一直在担心,p1指向地址为0处,一旦引用p1会不会出问题,请牢记这里我们并没有做dereference操作,即*p操作,而且获取b的地址实际上编译器是通过p1以及b的偏移量来计算得来的,也不涉及到dereference操作。你也可以理解为有一个'虚拟'的Foo object就存储在0×0这个地址上,呵呵。越过脑子中的那个阴影向前一步便会豁然开朗。

不进一步分析了,一般offsetof的实现如下:
#define offsetof(type, f) ((size_t)((char *)&((type *)0)->f – (char *)(type *)0))
有了上面的阐述相信理解这个宏定义应该不难。

用C++的写法:std::cout << reinterpret_cast<size_t>(&((reinterpret_cast<Foo*>(0))->p)) << std::endl;
不知道上面我的逻辑是否适合大家。^_^

设计心理学

其实说到'设计心理学',自己还没资格谈,按照'疯狂的时候'里的说法'自己还不够专业',今天说到它,是另有原因的,下面道来。

周末写了一篇未完的blog,今早趁机将其补充完整并予以发布,不过在发布时发现Blogbus的一处问题:即我在发布文章页面的分类下拉框中居然找不到我若干月前就已经增加了的分类,以致我无法选择对应的分类就发布了。事后我问及Blogbus的客服小伙:TTSummer,在一番问题陈述之后,TTSummer马上联系了开发人员,很快给了我答复:Blogbus的分类列表项最多支持20个Item,接着他说他们开发人员马上就修改这个问题,将最大表项数目增加到25个。在中午十分这个问题解决了。从这件事里看得出Blogbus的客服还是很好的,表扬一下TTSummer,呵呵。

之后我也和TTSummer谈了我对这件事的想法,因为我也是软件设计开发人员,也遇到过类似的问题。我不清楚Blogbus是如何实现这个列表的,也不知道列表项目多少是否影响系统资源的占用或者影响美观之类的,也不知道Bus的设计人员为什么初始时设置了20这个数字,但是在软件开发领域有这样一条潜规则:软件开发中最大的不变量就是变化。一切皆有可能,其实我在平时开发中也是有想当然的时候,比如认为用户不能怎么做,不会怎么做,但是事实上用户是不给你面子的,他偏偏就那么做了。TTSummer反驳了我一句,也许是从他客服的角度理解的吧:'有时候给用户的更多,反而会让用户无所适从',他还说到:'这个是设计心理学的问题咯'。就这样我们谈到了设计心理学。其实后来TTSummer给我解释了他们的这个列表数目是根据一段时间运行后的统计来修改的,这也算是折衷的一个好方法。但是理论上是应该允许用户无限增加的,blogbus在分类管理页面并没有给用户明确提示只允许添加20个分类表项,那么既然没有明确说明,我添加了第21个就应该给我显示出来,这里说来说去也涉及到了与用户交互设计的这个问题,不知道算不算是'设计心理学'的范畴。

如果是Blogbus的老用户,大家都知道blogbus的后台程序做了很多次升级,其中一次就是增加了分类功能和分类批量修改工具,因为此前的blogbus只是支持Tag,而不支持分类,像我这样的用户都是用Tag来替代分类对文章进行管理的。当分类功能被推出后,我势必要对我的文章重新进行分类管理,最简洁的方法就是做Tag到分类的一一对应。而Tag一般都有很多,超过20个也是很正常的事情,这时像Bus这样只支持20个分类表项的问题就会暴露,还好那时我的分类没有这么多,所以也就没有发现该问题,呵呵。

谈到'设计心理学',TTSummer说其正在看那本有名的'The Design of everyday things',中文翻译为:设计心理学,这本书我是只有电子版的,下了之后就扔在电脑里,也没有时间看,呵呵,没有时间啊,也没什么办法。今天还和TTSummer说到:'人长大后烦心事太多了,时间就这样被肢解了,除了必要的睡眠外,留给看书的时间就太少了'这样的牢骚话。^_^

平时和TTSummer的交流应该不算少,作为Bus的用户,在交流中逐渐体会到:服务的提供者和服务使用者需要共同改进,互相体谅,做到双赢,只有服务商提供更加优良的服务,服务使用者才会得更优良的服务;服务使用者体会到优良服务后,服务提供商的口碑越好,其使用者也就会越来越多,在IT行业口碑一样很重要;相反一出现问题就骂个不停,那可决不是解决问题的正确方法。

有些文不对题,看题目好象是一篇技术类文章,其实不然啊,呵呵。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats