Chuanbin 的个人资料Bruce's life照片日志列表 工具 帮助
5月25日

#pragma pack() 的使用

#pragma pack(4)
  class TestB
  {
  public:
    int aa;
    char a;
    short b;
    char c;
  };
  int nSize = sizeof(TestB);
  这里nSize结果为12,在预料之中。

  现在去掉第一个成员变量为如下代码:
  #pragma pack(4)
  class TestC
  {
  public:
    char a;
    short b;
    char c;
  };
  int nSize = sizeof(TestC);
  按照正常的填充方式nSize的结果应该是8,为什么结果显示nSize为6呢?

事实上,很多人对#pragma pack的理解是错误的。
#pragma pack规定的对齐长度,实际使用的规则是:
结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

具体解释
#pragma pack(4)
  class TestB
  {
  public:
    int aa; //第一个成员,放在[0,3]偏移的位置,
    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
    char c; //第四个,自身长为1,放在[8]的位置。
  };
这个类实际占据的内存空间是9字节
类之间的对齐,是按照类内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。
所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。
9按照4字节圆整的结果是12,所以sizeof(TestB)是12。


如果
#pragma pack(2)
    class TestB
  {
  public:
    int aa; //第一个成员,放在[0,3]偏移的位置,
    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
    char c; //第四个,自身长为1,放在[8]的位置。
  };
//可以看出,上面的位置完全没有变化,只是类之间改为按2字节对齐,9按2圆整的结果是10。
//所以 sizeof(TestB)是10。

最后看原贴:
现在去掉第一个成员变量为如下代码:
  #pragma pack(4)
  class TestC
  {
  public:
    char a;//第一个成员,放在[0]偏移的位置,
    short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
    char c;//第三个,自身长为1,放在[4]的位置。
  };
//整个类的大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6
//所以sizeof(TestC)是6。

-------------------------------------下面是代码测试,Solaris8.0,gcc 2.95-----------------------------------------

//一

#include <stdio.h>
#include <string.h>

#pragma pack(4)
class testc
{
public :
        int d;
        char a;
        char b;
        char c;
};

int main()
{
        int i = 0;
        char *p;
        testc t;
        memset(&t,0,sizeof(testc));
        t.a = 1;
        t.b = 2;
        t.c = 3;
        t.d = 4;
        printf(" size is %d \n",sizeof(testc));
        p = (char *)&t;
        for( i=0; i<sizeof(testc); i++ )
                printf(" %d",p[i]);
        printf("\n");
        return 0;
}

输出为:

bash-2.03$ gcc pragma.cpp -o pragma
bash-2.03$ ./pragma
 size is 8
 0 0 0 4 1 2 3 0

// 二

#pragma pack(4)
class testc
{
public :
        int d;
        char a;
        short b;
        char c;
};

输出为:

bash-2.03$ ./pragma
 size is 12
 0 0 0 4 1 0 0 2 3 0 0 0

// 三

#pragma pack(4)
class testc
{
public :
        int d;
        short a;
        char b;
        char c;
};

输出为:

bash-2.03$ gcc pragma.cpp -o pragma
bash-2.03$ ./pragma
 size is 8
 0 0 0 4 0 1 2 3

//四

#pragma pack(4)
class testc
{
public :
        short a;
        char b;
        char c;
};

输出为:

bash-2.03$ ./pragma
 size is 4
 0 1 2 3

// 五

#pragma pack(4)
class testc
{
public :
        char a;
        short b;
        char c;
};

输出为:

bash-2.03$ gcc pragma.cpp -o pragma
bash-2.03$ ./pragma
 size is 6
 1 0 0 2 3 0

//六

#pragma pack(4)
class testc
{
public :
        char a;
        char b;
        short c;
};

输出为:

bash-2.03$ gcc pragma.cpp -o pragma
bash-2.03$ ./pragma
 size is 4
 1 2 0 3

//七

class testc
{
public :
        int  d;
        char a;
        char b;
        short c;
};

输出为:

bash-2.03$ gcc pragma.cpp -o pragma
bash-2.03$ ./pragma
 size is 8
 0 0 0 4 1 2 0 3

5月20日

类的原理

有时我们可能会做出一些违背原理的事情,我们为什么要这么做?我们首先要明确我们的目标:是要创造更容易维护的系统。这些原理都是能够使我们创建出系统能力的工具。因此仔细考虑到各种原理是十分重要的,对于任何原理的违背都应该是有意识的设计选择。

我们创造了原理,是为我们的目标服务,而不是让我们受制于我们创造的原理。遵守原理,也要勇于突破原理,勇于创新。

1.      开方封闭原理(OCP  Open Closed Principle

对象类应该是开发的以便于扩充,又要是封闭的有利于修改。

2.      Liskov 替代原理(LSP  Liskov Substitution Principle

子类应该可以用来替代其基类

3.      依赖性倒置原理(DIP Dependency Inversion Principle

依赖于抽象类,不要依赖于具体类

   抽象耦合是指一个类不能与另一个类耦合,也不能和可实例化的类耦合。类该和其他基类或抽象基类耦合。这个概念实际上是LSP实现其灵活性所必须的方法,使DIP所必须的机制,也是OCP的核心思想。

4.        接口分离原理(ISP   Interface Separate Principle

多个专用接口由于一个单一的通用接口。

5.        构成重用原理(CRP Composite Reuse Principle)

对象构成物的多态性由于继承。

如果我们要在基类中定义缺省的行为的话,一定要保证这个行为适用于所有的派生类。

6.        最少知识原理(Principle of Lease KnowledgePLK

在一个类上的操作中,只有类本身、操作的参数对象、操作中创建的对象和类包含的实例对象等的操作可以被调用。

 

5月16日

关于atoi函数和itoa函数

今天在网山查找这2个函数的时候,看到了这2个函数在solaris平台下的源代码,惊异于别人算法的精悍和语法的精湛。自己仅仅只是才开始而已~
 
// Solaris下atoi源码

#define ATOI
#ifdef ATOI
 typedef int  TYPE;
 #define NAME atoi
#else
 typedef long TYPE;
 #define NAME atol
#endif

TYPE NAME(const char *p)
{
 TYPE   n;
 int    c,  neg = 0;
 unsigned char *up = (unsigned char *)p;
 
 if (!isdigit(c = *up))
 {
  while (isspace(c))
   c = *++up;
  switch (c)
  {
  case '-':
   neg++;
  /* FALLTHROUGH */
  case '+':
   c = *++up;
   }
  if (!isdigit(c))
   return (0);
 }
 for (n = '0' - c; isdigit(c = *++up); )
 {
  n *= 10; /* two steps to avoid unnecessary overflow */
  n += '0' - c; /* accum neg to avoid surprises at MAX */
 }
 return (neg ? n : -n);
}

 

 
>// Solaris平台itoa源码

#char *itoa(n, base)
 long n;   /* abs k16 */
 int base;
{
 register char *p;
 register int minus;
 static char buf[36];

 p = &buf[36];
 *--p = '';
 if (n < 0)
 {
  minus = 1;
  n = -n;
 }
 else
  minus = 0;
 if (n == 0)
  *--p = '0';
 else
  while (n > 0)
  {
   *--p = "0123456789abcdef"[n % base];
   n /= base;
  }
 if (minus)
  *--p = '-';
 return p;
}