博文

Data Alignment(2006-05-22 12:18:00)

摘要:#pragma pack Preface     很多 CPU ,如基于 Alpha, IA-64, MIPS, 和 SuperH 体系的,拒绝读取未对齐数据。当一个程序要求其中之一的 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。举个例子,在 ARM, MIPS, 和 SH 硬件平台上,当操作系统被要求存取一个未对齐数据时默认通知应用程序一个异常。

对齐性
        对齐性是一种内存地址的特性,表现为内存地址模上 2 的幂。例如,内存地址 0x0001103F 模 4 结果为 3 ;这个地址就叫做与 4n + 3 对齐, 4 指出了所选用的 2 的幂的值。内存地址的对齐性取决于所选择的关于 2 的幂值。同样的地址模 8 结果为 7 。

       一个内存地址符合表达式 Xn + 0 ,那么就说该地址对齐于 X 。

       CPU 执行指令就是对存储于内存上的数据进行操作,这些数据在内存上是以地址为标记的。对于地址来说,单独的数据会有其占用内存的字节数。如果它的地址对齐于它的字节数,那么就称作该数据自然对齐,否则称为未对齐。例如,一个标记 8 字节的浮点数据的地址对齐于 8 ,那么这个数据就自然对齐。

数据对齐性的编译处理
       设备编译器以一种防止造成数据未对齐的方式来对数据进行地址分配。

       对于单个的数据类型,编译器为其分配的地址是数据类型字节数的倍数。因此,编译器分配给 long 型变量的地址为 4 的倍数,就是说以 2 进制表示地址的话,最后两位为 0 。

       另外,编译器以一种自然对齐每个结构成员的方式来填充结构体。参看下面的代码里面的结构体 struct x_ 。......

阅读全文(5126) | 评论:1

关于CString(2005-12-12 15:37:00)

摘要:通过阅读本文你可以学习如何有效地使用 CString。
  CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。
  使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册,但囊括了大部分常见基本问题。
这篇文章包括以下内容:
CString 对象的连接
格式化字符串(包括 int 型转化为 CString )
CString 型转化成 int 型
CString 型和 char* 类型的相互转化
char* 转化成 CString
CString 转化成 char* 之一:使用LPCTSTR强制转化
CString 转化成 char* 之二:使用CString对象的GetBuffer方法
CString 转化成 char* 之三: 和控件的接口
CString 型转化成 BSTR 型;
BSTR 型转化成 CString 型;
VARIANT 型转化成 CString 型;
载入字符串表资源;
CString 和临时对象;
CString 的效率;
总结
下面我分别讨论。
1、CString 对象的连接
  能体现出 CString 类型方便性特点的一个方面就字符串的连接,使用 CString 类型,你能很方便地连接两个字符串,正如下面的例子:
CString gray("Gray");
CString cat("Cat");
CString graycat = gray + cat;
要比用下面的方法好得多:
char gray[] = "Gray";
char cat[] = "Cat";
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
......

阅读全文(5592) | 评论:0

确定一个对象在使用前已经被初始化(2005-11-23 12:39:00)

摘要:C++ 看上去在对象的初始化方面变化无常。例如,如果你这样做,

int x;

在某些情形下,x 会被初始化(0),但是在其它情形下,也可能没有。如果你这样做,

class Point {

  int x, y;

};
...
Point p;

p 的数据成员有时会被初始化(0),但有时没有。如果你从一个不存在未初始化对象的语言来到 C++,请注意这个问题,因为它非常重要。

读取一个未初始化的值会引起未定义行为。在一些平台上,读一个未初始化的值会引起程序中止,更可能的情况是得到一个你所读的那个位置上的随机值,最终导致不可预测的程序行为和恼人的调试。

现在,有一些关于什么时候进行对象的初始化什么时候不进行的规则的描述。不幸的是,这些规则很复杂——我觉得它复杂得无法记住。通常,如果你使用 C++ 的 C 部分(参见 Item 1),而且初始化可能会花费一些运行时间,它就不一定会进行。如果你使用 C++ 的 non-C 部分,事情会有些变化。这就是为什么一个数组(C++ 的 C 部分)不能确保它的元素被初始化,但是一个 vector(C++ 的 STL 部分)就能够确保。

处理这种表面上看起来不确定的事情的最好方法就是总是在使用之前初始化你的对象。对于内建类型的非成员对象,需要你手动做这件事。例如:

int x = 0;                                // manual initialization of an int

const char * text = "A C-style string";   // manual initialization......

阅读全文(2607) | 评论:0

利用C++模板技术支持多种计算策略(2005-11-17 15:27:00)

摘要:任何有经验的程序员和软件设计师都知道,在软件开发中最常见的困境的并不是在问题面前束手无策,而是在一大堆好像都可行的解决策略中选择一个。软件设计就是选择的过程,其困难之处也就在于你得不断地、小心翼翼地做出各种选择。在传统的程序设计模式下,每一次选择都是不可逆转的,你做出了选择,那么随后的开发都将会受到这次选择的剧烈影响。当你发现自己当初的选择错误的时候,可能一切都已经为时太晚。这有点像是走迷宫,而走迷宫时如果你发觉走进了死胡同,还可以掉头换一条路线。但在软件开发中,更换计算策略的成本太高,几乎是无法承受的。如果能够有一种软件设计方法,使更换计算策略的成本大大降低,使你有更多的“悔棋”的机会,那么软件设计的难度就可以大大降低,质量就会得到很大的提高。
另一个常常出现而且令人烦恼的问题是这样的,我们在软件的开发中经常会遇到同类型的问题反复地以不同面貌出现,每次都让你在直觉上感到这几个问题好像差不多。但是当你满怀信心的企图实现一个可重用组件,一劳永逸地解决问题的时候,你就会沮丧地发现,一些细节上的不同使你很难、甚至根本没有办法实现这样一个可重用的组件。于是你不得不一次次地重写类似的代码,使得程序冗长,难于维护。如果我们有一种高度抽象的软件开发技术,把所有细节上的不同都抽象起来,包装在一个统一的接口背后,那么我们就有能力来达成更高程度的代码可重用性,写出适用性更强的软件组件。显然,在这些细节里,类型是一个重要方面,而另一个重要方面就是计算策略。
综合这两点,我们可以看到,把计算策略从一个软件组件中抽象出来将会非常有益。这当然不是一件容易的事情,不光需要在设计上做大量分析,还需要一些特别的编程技术。本文试图用C++提供的模板技术,来给出解决这个问题的一种思路。
1.    一个实际例子
我拿一个手头的例子来说明,在施工进度控制系统里,有一个这样的功能:在已知各种必要信息的情况下,需要估算进行某项工作需要花费的时间。在这里,我的角色不是应用程序的开发者,而是支持组件的开发者。也就是说,我来提供组件,由客户程序员具体使用,完成特定的任务。在这样一个简单的任务里,充满了各种选择,我们把这些选择简述如下:
基本计算模型:可以采用经验估计法,也可以用公式计算法,用户还可能有其他更贴切的计算模型。
精确......

阅读全文(3999) | 评论:0

怎样在 C++ 中计算对象的实例化次数(2005-11-16 13:38:00)

摘要:
                     Objects Counting in C++

  在 C++ 中,对某个 class 所产生出来的 objects保持正确的计数,是办得到的,除非你面对一些疯狂份子。
译文保留原作时态,并未修改。以下是译文所采用的几个特别请读者注意的术语:
client :客端。
type:型别。为避免和其他近似术语混淆,本文译为「型别」而非「类型」。
instantiated:具现化。「针对一个 template,具体实现出一份实体」的意思。
instance:实体。「案例」是绝对错误的译法。
parameter:叁数。或称型式叁数,形叁。
argument:引数。或称实质叁数,实叁。

至於 class, object, data member, member function, constructor, destructor, template 等术语,皆保留不译。

有时候,容易的事情虽然容易,但它们还是隐藏着某种微妙。举个例子,假设你有个 class名为 Widget,你希望有某种办法找出程式执行期间究竟存在着多少个 Widget objects。方法之一(不但容易实作而且答案正确)就是为 Widget 准备一个 static 计数器,每当Widget constructor 被呼叫,就将该计数器加一,每当Widget destructor 被呼叫,就将该计数器减一。此外你还需要一个 static 成员函式 howMany( ),用来回报目前存在多少个 Widget objects。如果 Widget 什麽都没做,单单只是追踪其objects个数,那麽它看起来大约像这样:

class Widget {
public:
    Widget() { ++count; }
   &n......

阅读全文(6747) | 评论:2