位域定义

测试环境:vc6.0

struct mybitfields
{
unsigned short a : 4;
unsigned short b : 5;
unsigned short c : 7;
} test;
=> sizeof(test) == 2;

struct mybitfields
{
unsigned char a : 4;
unsigned char b : 5;
unsigned char c : 7;
} test;
=> sizeof(test) == 3;

struct mybitfields
{
unsigned char a : 4;
unsigned short b : 5;
unsigned char c : 7;
} test;
=> sizeof(test) == 6;

struct mybitfields
{
unsigned short a : 4;
unsigned char b : 5;
unsigned char c : 7;
} test;
=> sizeof(test) == 4;

struct mybitfields
{
unsigned char a : 4;
unsigned char b : 5;
unsigned short c : 7;
} test;
=> sizeof(test) == 4;

struct mybitfields
{
unsigned char a : 4;
unsigned int b : 5;
unsigned short c : 7;
} test;
=> sizeof(test) == 12;

构造函数出现异常时的行为

char *g_pszTest = NULL;

class B
{
public:
B()
{
cout << "B constructed" << endl;
}

~B()
{
cout << "B destructed" << endl;
}
};


class C
{
public:
C()
{
cout << "C constructed" << endl;
}

~C()
{
cout << "C destructed" << endl;
}
};

class A : public B
{
public:
A()
{
cout << "A constructed" << endl;
g_pszTest = new char[20];
strcpy(g_pszTest, "before throw");
throw 1;
strcpy(g_pszTest, "after throw");
}

~A()
{
cout << "A destructed" << endl;
if (g_pszTest != NULL)
{
cout << g_pszTest << endl;
}
}

private:
C m_c;
};

该段程序代码输出为:
B constructed
C constructed
A constructed
C destructed
B destructed
before throw

1. 构造函数throw之后的代码不会被执行;
2. throw了异常的类的析构函数不会被执行,但throw之前已经构造的对象的析构函数会被正常执行。

构造析构的顺序问题

#include 
using namespace std;

class M
{
public:
M()
{
cout << "M constructed" << endl;
}

~M()
{
cout << "M destructed" << endl;
}
};

class N
{
public:
N()
{
cout << "N constructed" << endl;
}

~N()
{
cout << "N destructed" << endl;
}
};

class A : virtual public M
{
public:
A()
{
cout << "A constructed" << endl;
}

~A()
{
cout << "A destructed" << endl;
}
};

class B : virtual public M
{
public:
B()
{
cout << "B constructed" << endl;
}

~B()
{
cout << "B destructed" << endl;
}
};

class C : public B, public A
{
public:
C()
{
cout << "C constructed" << endl;
}

~C()
{
cout << "C destructed" << endl;
}

private:
N m_n;
};

int main()
{
// 构造的几点原则:
// 1. 先构造父类,然后构造子类
// 2. 虚拟继承,虚基类只会被构造一次
// 3. 多重继承,写在前面的先被构造(class C : public B, public A,所以B先于A被构造)
// 4. 先构造基类,然后构造成员对象(m_n)
// 所以,这里将依次输出:
// M constructed
// B constructed
// A constructed
// N constructed
// C constructed
C c;  

// 析造的重要原则:析构的顺序与构造的顺序刚好相反!
// 所以,这里将依次输出:
// C destructed
// N destructed
// A destructed
// B destructed
// M destructed
return 0;
}

boost简介

boost是一个准标准库,相当于STL的延续和扩充,它的设计理念和STL比较接近,都是利用泛型让复用达到最大化。不过对比STL,boost更加实用。  STL集中在算法部分,而boost包含了不少工具类,可以完成比较具体的工作。 
boost主要包含一下几个大类:字符串及文本处理、容器、迭代子(Iterator)、算法、函数对象和高阶编程、泛型编程、模板元编程、预处理元编程、并发编程、数学相关、纠错和测试、数据结构、输入/输出、跨语言支持、内存相关、语法分析、杂项。 有一些库是跨类别包含的,就是既属于这个类别又属于那个类别。 
在文本处理部分,conversion/lexcial_cast类用于”用C++”的方法实现数字类型和字符串之间的转换。 主要是替代C标准库中的 atoi、 itoa之类的函数。当然其中一个最大的好处就是支持泛型了。 
format库提供了对流的”printf-like”功能。printf里使用%d、%s等等的参数做替换的方法在很多情况下还是非常方便的,STL的iostream则缺乏这样的功能。format为stream增加了这个功能,并且功能比原始的printf更强。 
regex,这个不多说了,正则表达式库。如果需要做字符串分析的人就会理解正则表达式有多么有用了。 
spirit,这个是做LL分析的框架,可以根据EBNF规则对文件进行分析。(不要告诉我不知道什么是EBNF)。做编译器的可能会用到。一般人不太用的到。 
tokenizer库。我以前经常在CSDN上看到有人问怎么把一个字符串按逗号分割成字符串数组。也许有些人很羡慕VB的split函数。现在,boost的tokenizer也有相同的功能了,如果我没记错的话,这个tokenizer还支持正则表达式,是不是很爽? 
array: 提供了常量大小的数组的一个包装,喜欢用数组但是苦恼数组定位、确定数组大小等功能的人这下开心了。 
dynamic_bitset,动态分配大小的bitset,我们知道STL里有个bitset,为位运算提供了不少方便。可惜它的大小需要在编译期指定。现在好了,运行期动态分配大小的bitset来了。 
graph。提供了图的容器和相关算法。我还没有在程序中用到过图,需要用的人可以看看。 
multi_array提供了对多维数组的封装,应该还是比较有用的。 
并发编程里只有一个库,thread,提供了一个可移植的线程库,不过在Windows平台上我感觉用处不大。因为它是基于Posix线程的,在Windows里对Posix的支持不是很好。 
接下来的 数学和数值 类里,包含了很多数值处理方面的类库,数学类我也不太熟,不过这里有几个类还是很有用的,比如rational分数类,random随机数类,等等。 
static_assert,提供了编译器的assert功能。 
test库,一个单元测试框架,非常不错。 
concept_check提供了泛型编程时,对泛型量的一点检查,不是很完善,不过比没有好。 
数据类型类any,一个安全的可以包含不同对象的类。把它作为容器的元素类型,那么这个容器就可以包含不同类型的元素。比用void *要安全。 
compressed_pair,跟STL里的pair差不多。不过对空元素做了优化。 
tuple,呵呵,也许是某些人梦寐以求的东西。可以让函数返回多个值。 
跨语言支持:python,呵呵,好东东啊,可以将C++的类和函数映射给python使用。以下为几个CSDN上的关于boost.python的中文资料: 
   pool:内存池,呵呵,不用害怕频繁分配释放内存导致内存碎片,也不用自己辛辛苦苦自己实现了。 
   smart_ptr:智能指针,这下不用担心内存泄漏的问题了吧。不过,C++里的智能指针都还不是十全十美的,用的时候小心点了,不要做太技巧性的操作了。 
   date_time,这个是平台、类库无关的实现,如果程序需要跨平台,可以考虑用这个。 
   timer,提供了一个计时器,虽然不是Windows里那种基于消息的计时器,不过据说可以用来测量语句执行时间。 
   uitlity里提供了一个noncopyable类,可以实现”无法复制”的类。很多情况下,我们需要避免一个类被复制,比如代表文件句柄的类,文件句柄如果被两个实例共享,操作上会有很多问题,而且语义上也说不过去。一般的避免实例复制的方法是把拷贝构造和operator=私有化,现在只要继承一下这个类就可以了,清晰了很多。 
    value_initialized:数值初始化,可以保证声明的对象都被明确的初始化,不过这个真的实用吗?似乎写这个比直接写初始化还累。呵呵,仁者见仁了。 
   这里面除了regex、python和test需要编译出库才能用,其他的大部分都可以直接源代码应用,比较方便。其实这些库使用都不难。最主要的原因是有些库的使用需要有相关的背景知识,比如元编程、STL、泛型编程等等。 

scanf、fgets和cin

    偶然一个机会,在linux下发现了这个问题,现在列举他们的区别:
    1. scanf用于格式化接受用户输入,他与cin区别不大,用惯了c的哥们肯定更倾向与直接用scanf做格式化输入。格式化输入是格式化输出的逆向过程,大家想象下面这个scanf会是什么现象呢?
    char s[40] = “12345678”;
    scanf(“%s
“, s);
    你调试一下就会发现,这个时候你必须敲两下回车,scanf才会返回,两个回车符中,第一个就是格式化串”%s
“中的
,第二个就是系统默认的字符串结束符。
    2. fgets用于获取文件流中的字符,注意,
当然也算字符哟,而且文件流不仅仅包括磁盘上的文件,也包括内存文件,标准输入(stdin)、标准输出(stdout)等等,在linux下,文件流更是包含了串口、网络、驱动程序等等抽象的输入输出接口。
    呵呵,别扯远了,看看下面这个例子,就可以很好的理解fgets和scanf的区别了:
    scanf(“%s”, s);
    cout << strlen(s) << , << sizeof(s) <<  ;
    fgets(s, 39, stdin);
    cout << strlen(s) << , << sizeof(s) <<  ;
    也许你会想,第一个scanf用于接受用户输入,用户敲回车后,第二个fgets会接受用户的另一个输入,但是结果不是这样的,比如你这样敲:
    > 123456回车
    那么程序会一直执行到第四条语句,如果单步调试,你会发现当你回车后,s首先为123456

循环是否一定该合并?(转载)

摘自《游戏之旅--我的编程感悟》

程序1:
for (int i = 0; i < 100; i++)
{
   do_something_1();
   do_something_2();
}

程序2:
for (int i = 0; i < 100; i++)
{
   do_something_1();
}

for (int i = 0; i < 100; i++)
{
   do_something_2();
}

    如果do_something_1()和do_something_2()这两个函数没有关联, 则程序1和程序2的运行结果相同.哪个程序更好?  或许有很多讲究效率的程序远喜欢第一种, 很多人喜欢在一个for循环中干太多的事情, 或许他们想当然的认为, for循环累加i也是一种时间消耗, 如果能减少对i的操作也是一种优化.但实际上事与愿违,两段程序的性能比较取决于do_something_1和do_something_2的具体实现.

    如果两个函数都实现的很简单, 则通常程序1会比较有效率.而如果两个函数都干了较为复杂的事情,那么程序2会变得比程序1高效.导致这个结果的是cpu的代码cache机制.通常, 程序代码第一次运行的时候,速度会比较慢(比如for循环的第一次进入).这存在一个代码从内存加载的时间,代码用到的内存数据的加载时间、还有分支预测缓存的建立,甚至还有cpu对指令的解码时间(对于现代cpu, 为了兼容以前的指令集且保持高效率,在内部需要使用一套高效的微操作指令的同时,需要做从外部的指令编码到内部微指令的转换工作)。如果循环体内涉及到的代码超过了cpu的代码缓存的容量(通常是8k,到p4加到了16k),就使的循环中代码每次的进入都要重新作一系列缓慢的操作,如果我们能尽量维持循环内的代码在一个较小的尺寸,就能提高效率。

    如果do_something_1()和do_something_2()干的事情逻辑上联系不大,仅仅是想把两个函数各自循环调用若干次,则从软件设计角度讲,程序1也是个糟糕的设计。

网络字节序和主机字节序(转载)

    谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢?

    其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。 
    用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

Big Endian

 低地址                              高地址
   —————————————–>
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     12     |      34    |     56      |     78    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

低地址                                    高地址
   —————————————–>
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     78     |      56    |     34      |     12    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&*,见鬼去吧 -_-|||

    为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

    无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。

    Intel 8080 和 Motorola 6800还有一个有趣的不同点:在两种微处理器中,LDA指令都是从一个特定的地址处装载到累加器。例如,在8080中,下列字节序列:

screen.width/2)this.width=screen.width/2; >

    将把存储在地址347Bh处的字节装载到累加器。现在把上述指令与6800的LDA指令相比较,后者采用称作6800的扩展地址模式:

screen.width/2)this.width=screen.width/2; >

    该字节序列把存储在地址7B34h处的字节装载到累加器A中。

    这种不同点是很微妙的。当然,你也可能认为它们的操作码不同:对8080来说是3Ah,对6800来说是B6h。但主要是这两种微处理器处理紧随操作码后的地址是不同的,8080认为低位在前,高位在后;6800则认为高位在前,低位在后。这种Intel和Motorola微处理器保存多字节数时的根本不同从没有得到解决。直到现在,Intel微处理器在保存多字节数时,仍是最低有效字节在前(即在最低存储地址处);而Motorola微处理器在保存多字节数时,仍是最高有效字节在前。

    这两种方法分别叫作little-endian(Intel方式)和big-endian(Motorola方式)。辩论这两种方式的优劣可能是很有趣的,不过在此之前,要知道术语big-endian来自于JonathanSwift的《Gulliver’sTravels》,指的是Lilliput和Blefuscu在每次吃鸡蛋前都要互相碰一下。这种辩论可能是无目的的。先不说哪种方法在本质上说是不是正确的,但这种差别的确当在基于little-endian和big-endian机器的系统之间共享信息时会带来附加的兼容性问题。

shit——MaxplusII

    很久没上来写日志了,最近几天在用VHDL模拟一个串口,搞得头昏脑胀的。
    不得不发点牢骚的是Maxplus II这个编译器,其错误定位功能实在是糟糕得可以,分明是一个非常低级的错误,但它给的信息经常不知道所以然,双击错误信息,它总是不能定位到错误的那一行,然后要自己去通读整个程序,一行行的排查,效率奇低!比如一个signal,在被引用前没有给他赋值,它报的错就是貌似63648Missing source,双击它,它跑到vhd文件的第一行,让你觉得莫名其妙!

    再说说VHDL,虽然以前也搞过,但这次才算是真正的潜心研究了VHDL,我的感觉是,使用VHDL做硬件仿真才算是真正的做硬件,因为它真正体现了很多硬件特性,比如,为什么一个信号不能有多个信号源呢?因为信号有真实的物理意义,如果一根导线上连多个输出线会是什么结果呢,这样联想一下,才能理解为什么会有这样的限制。
    用C语言用习惯的人可能会经常犯低级错误,就像我这样,总结起来,犯的最多的低级错误是:
    1. 刚才已经说过,一个信号(SIGNAL)不能有多个信号源;
    2. 对于PORT,它的各种类型是有严格规定的,IN、INOUT、OUT、BUFFER之间的区别必须搞清楚,如果有OUT属性,则project中必须对它有写的操作,OUT型端口不能再被读入,如果要读入OUT型端口,必须设置成BUFFER型端口;
    3. 不是所有情况下都可以用IF….ELSE,比如IF中如果对上升延或者下降延进行了判断,那就不能再用ELSE子句:
       IF aEVENT AND a = 0 THEN
           ….
       ELSE
           ….
       END IF;
       这样写肯定会报错的,因为VHDL不支持对同一脉冲的上升延和下降延同时进行判断。

SPI、I2C、UART三种串行总线协议的区别

第一个区别当然是名字:
    SPI(Serial Peripheral Interface:串行外设接口);
    I2C(INTEL IC BUS:英特尔IC总线)
    UART(Universal Asynchronous Receiver Transmitter:通用异步收发器)

第二,区别在电气信号线上:
    SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)。SPI总线可以实现 多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。主从设备间可以实现全双工通信,当有多个从设备时,还可以增加一条从设备选择线。
    如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO),一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。

    I2C总线是双向、两线(SCL、SDA)、串行、多主控(multi-master)接口标准,具有总线仲裁机制,非常适合在器件之间进行近距离、非经常性的数据通信。在它的协议体系中,传输数据时都会带上目的设备的设备地址,因此可以实现设备组网。
    如果用通用IO口模拟I2C总线,并实现双向传输,则需一个输入输出口(SDA),另外还需一个输出口(SCL)。(注:I2C资料了解得比较少,这里的描述可能很不完备)

    UART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件上由两根线,一根用于发送,一根用于接收。
    显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。

第三,从第二点明显可以看出,SPI和UART可以实现全双工,但I2C不行;

第四,看看牛人们的意见吧!
    wudanyu:I2C线更少,我觉得比UART、SPI更为强大,但是技术上也更加麻烦些,因为I2C需要有双向IO的支持,而且使用上拉电阻,我觉得抗干扰能力较弱,一般用于同一板卡上芯片之间的通信,较少用于远距离通信。SPI实现要简单一些,UART需要固定的波特率,就是说两位数据的间隔要相等,而SPI则无所谓,因为它是有时钟的协议。
    quickmouse:I2C的速度比SPI慢一点,协议比SPI复杂一点,但是连线也比标准的SPI要少。

TagDemoIII不爽的地方

1. View和Element的属性和状态有诸多重复,比如Scale(宽高比),如果View和Element是一对一的关系,那么Scale应该是统一的,但这里出现了两个Scale,很容易不一致;