2010年2月28日星期日

C++ Keywords: typedef

(1)定义已知类型的别名

In C++, the typedef keyword allows you to create an alias for a data type. The formula to follow is:

typedef [attributes] DataType AliasName;

The typedef keyword is required. The attribute is not.

例子:
typedef short SmallNumber;
typedef unsigned int Positive;
typedef double* PDouble;
typedef string FiveStrings[5];


1. In typedef short SmallNumber, SmallNumber can now be used as a data type to declare a variable for a small integer.
2. In the second example, typedef unsigned int Positive, The Positive word can then be used to declare a variable of an unsigned int type.
3. After typedef double* PDouble, the word PDouble can be used to declare a pointer to double.
4. By defining typedef string FiveStrings[5], FiveStrings can be used to declare an array of 5 strings with each string being of type string.

SmallNumber temperature = -248;
Positive height = 1048;
PDouble distance = new double(390.82);
FiveStrings countries = { "Ghana", "Angola", "To“,"Tunisia", "Cote d'Ivoire" };

值得注意的写法
typedef char* PSTR, CHAR;
CHAR为char型,所以更好的写法是
typedef char *PSTR, CHAR; 或者 typedef char CHAR, *PSTR;

(2) 定义函数类型别名

The typedef keyword can also be used to define an alias for a function. In this case, you must specify the return type of the function and its type(s) of argument(s), if any. Another rule is that the definition must specify that it is a function pointer。

例子:
typedef double (*Addition)(double value1, double value2);
                          
double Add(double x, double y)
{
       double result = x + y;
        return result;
}

Addition plus;
plus = Add;
plus(3855.06, 74.88);

例子2:(msdn上)

The following example provides the type DRAWF for a function returning no value and taking two int arguments:

 typedef void DRAWF( int, int );

After the above typedef statement, the declaration

 DRAWF box; 

would be equivalent to the declaration

 void box( int, int );

(3)
总结typedef的语法为:

More generally, after the word “typedef”, the syntax looks exactly like what you would do to declare a variable of the existing type with the variable name of the new type name. Therefore, for more complicated types, the new type name might be in the middle of the syntax for the existing type. For example:

    typedef char (*pa)[3]; // "pa" is now a type for a pointer to an array of 3 chars
    typedef int (*pf)(float); // "pf" is now a type for a pointer to a function which takes 1 float argument and returns an int

C语言函数入栈顺序与可变参数函数

函数调用时如果出现堆栈异常,十有八九是由于函数调用约定不匹配引起的。比如动态链接库a有以下导出函数:

long MakeFun(long lFun);

动态库生成的时候采用的函数调用约定是__stdcall,所以编译生成的a.dll中函数MakeFun的调用约定是_stdcall,也就是函数调用时参数从右向左入栈,函数返回时自己还原堆栈。现在某个程序模块b要引用a中的MakeFun,b和a一样使用 C++方式编译,只是b模块的函数调用方式是__cdecl,由于b包含了a提供的头文件中MakeFun函数声明,所以MakeFun在b模块中被其它调用MakeFun的函数认为是__cdecl调用方式,b模块中的这些函数在调用完MakeFun当然要帮着恢复堆栈啦,可是MakeFun已经在结束时自己恢复了堆栈,b模块中的函数这样多此一举就引起了栈指针错误,从而引发堆栈异常。宏观上的现象就是函数调用没有问题(因为参数传递顺序是一样的),MakeFun也完成了自己的功能,只是函数返回后引发错误。解决的方法也很简单,只要保证两个模块的在编译时设置相同的函数调用约定就行了。

在了解了函数调用约定和函数的名修饰规则之后,再来看在C++程序中使用C语言编译的库时经常出现的LNK2001错误就很简单了。还以上面例子的两个模块为例,这一次两个模块在编译的时候都采用__stdcall调用约定,但是a.dll使用C语言的语法编译的(C语言方式),所以a.dll的载入库a.lib中MakeFun函数的名字修饰就是“_MakeFun@4”。b包含了a提供的头文件中MakeFun函数声明,但是由于b采用的是C++语言编译,所以MakeFun在b模块中被按照C++的名字修饰规则命名为“?MakeFun@@YGJJ@Z”,编译过程相安无事,链接程序时c++的链接器就到a.lib中去找“?MakeFun@@YGJJ@Z”,但是a.lib中只有“_MakeFun@4”,没有“?MakeFun@@YGJJ@Z”,于是链接器就报告:

error LNK2001: unresolved external symbol ?MakeFun@@YGJJ@Z

解决的方法和简单,就是要让b模块知道这个函数是C语言编译的,extern"C"可以做到这一点。一个采用C语言编译的库应该考虑到使用这个库的程序可能是C++程序(使用C++编译器),所以在设计头文件时应该注意这一点。通常应该这样声明头文件:

#ifdef _cplusplus
extern "C" {
#endif

long MakeFun(long lFun);

#ifdef _cplusplus
}
#endif

这样C++的编译器就知道MakeFun的修饰名是“_MakeFun@4”,就不会有链接错误了。

许多人不明白,为什么我使用的编译器都是VC的编译器还会产生“error LNK2001”错误?其实,VC的编译器会根据源文件的扩展名选择编译方式,如果文件的扩展名是“.C”,编译器会采用C的语法编译,如果扩展名是 “.cpp”,编译器会使用C++的语法编译程序,所以,最好的方法就是使用extern "C"。

==============================================
大家会注意到,像gcc之类的编译器也是采用__cdecl方式,这样对于printf(“%d %d“,i++,i++)之类的输出结果的疑问就迎刃而解了。

另外引出的一个问题是C语言的变参数函数,对于这部分估计很少大学会涉及到,但这的确是C语言很常见的一个特性,比如printf等。上面提到对于可变参数的支持,需要由函数调用者负责清除栈中的函数参数,但为什么非要这样才可以支持变参数呢?函数调用所涉及到的参数传递是通过栈来实现的,子函数从栈中读取传递给它的参数,如果是从左向右压栈的话那么子函数的最后一个参数在栈顶,然后依次是倒数第二个参数......;如果是从右向左压栈的话那么子函数的第一个参数在栈顶,然后依次是第二个参数......,压栈的顺序问题决定了子函数读取其参数的位置。对于变参数的函数本身来讲,并不知道有几个参数,需要某些信息才能知道,对于printf来讲,就是从前面的格式化字符串fmt来分析出来,一共有几个参数。所以被调函数返回清理栈的方式,对于可变参数是无法实现的,因为被调函数不知道要弹出参数的数量。而对于函数调用着,自己传递给被调函数多少参数(通过把参数压栈)当然是一清二楚的,这样被调函数返回后的堆栈清理也就可以做到准确无误了(不会多也不会少地把参数清理干净)。
===============================================

Solution of fatal error RC1004: unexpected end of file found

After you modified your reource.h, you may got this error:

fatal error RC1004: unexpected end of file found.

It's a bug of the resource compiler.

The solution is adding a blank line or two to the end of the file  and build again.

2010年1月15日星期五

visual studio 2008 试用版评估期已结束的解决方法

启动visual studio 2008后显示对话框:visual studio的试用版评估期已结束。

 解决方法如下:

VS2008中英文正式版序列号
1.Visual Studio 2008 Professional Edition:
XMQ2Y-4T3V6-XJ48Y-D3K2V-6C4WT
2.Visual Studio 2008 Team Test Load Agent:
WPX3J-BXC3W-BPYWP-PJ8CM-F7M8T
3.Visual Studio 2008 Team System:
PYHYP-WXB3B-B2CCM-V9DX9-VDY8T
4.Visual Studio 2008 Team Foundation Server:
WPDW8-M962C-VJX9M-HQB4Q-JVTDM
-----------------------------------------------------------------
把90天试用版改为正式版,二种方法:
1. 把Setup\setup.sdb文件中的
    [Product Key]
    T2CRQGDKBVW7KJR8C6CKXMW3D
  改成
    [Product Key]
    PYHYPWXB3BB2CCMV9DX9VDY8T
2.安装完成后,在“控制面板”中启动“添加删除程序”,选中Vs2008,点击“更改、删除”,
输入序列号:PYHYP-WXB3B-B2CCM-V9DX9-VDY8T

2009年12月27日星期日

全局对象的析构顺序

开宗明义:全局对象的析构顺序是不确定的,最好不要在程序逻辑中依赖这个顺序(免得出现移植问题)。

问题关注的焦点主要集中在:"cout是否会最先构造、最后析构"?

 ★关于老式编译器(03年之前推出)
  由于老式编译器在标准发布之前推出,因此对标准的实现不够好。
  老式编译器的典型代表就是VC++ 6.0。其实已经有很多人碰到了"VC6的cout提前析构"的问题。另外,如果你手头上正好有VC6,可以试试附在本文末尾的示例1的代码,看是否能够正常打印,就知道了。
  
  ★关于新式编译器(03年之后推出)
   那么新式编译器是否就完美地支持C++ 03标准呢?所以今天特地试验了手头能找到的几个C++编译器。看看它们是否能够保证cout最先构造、最后析构。另外,为了给这些新式编 译器增加点挑战,我把上述的示例1代码稍微修改了一下,成为示例2代码(说实话,这么写代码确实有点夸张),也附在本文末尾。
  手头的几种编译器测试下来,结果如下:
----------------------------
  操作系统 编译器版本      示例1  示例2
  Win2000  VC 7.1       OK   OK
  RHEL3   GCC 3.2.3      OK   OK
  Win2000  GCC 3.4.2(MingW)  OK   启动时崩溃
  Win2003  GCC 3.4.4(Cygwin)  OK   启动时崩溃
----------------------------
  对于示例2代码造成的崩溃,经过简单排查,基本可以推断是cout滞后构造导致(也就是用到cout时,它还没生出来)。而且GCC 3.4.2版本是2004年出品的,应该不算太老啊(至少03标准已经发布一年了)。从上面的结果看,Linux上版本较老的GCC反而没有问题。所以我猜测或许GCC在Windows上的移植版本有这个缺陷(仅仅是猜测啊)。

 ★总结
  根据上述的情况,建议:如果你的代码有全局对象,并且你的代码可能会跨编译器,那就避免在全局对象的构造函数和析构函数中使用标准流类库的那八个玩意儿(包括cout、cerr、clog等)。
  毕竟这八个全局对象,都有对应的标准C替代品,并不是不可替代的嘛。大伙儿犯不着冒险嘛。如果你确实舍不得流式操作符(<<和>>)或者确实看不惯printf的变参,你可以用字符串流先格式化好,再用标准C的函数输出嘛(也就多一两行代码而已嘛)。

// ========示例1代码========
#include <iostream>
using namespace std;

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

~CFoo()
{
cout << "~CFoo" << endl;
}
};

CFoo g_foo;

int main()
{
return 0;
}

// ========示例2代码========
class CFoo
{
public:
CFoo();
~CFoo();
};

CFoo g_foo;

#include <cstdio>
#include <iostream>
using namespace std;

CFoo::CFoo()
{
puts("puts CFoo");
cout << "cout CFoo" << endl;
}

CFoo::~CFoo()
{
cout << "cout ~CFoo" << endl;
puts("puts ~CFoo");
}

int main()
{
return 0;
}

2009年10月26日星期一

ubuntu 下 Tor,你懂的

翻墙有风险,带套保平安

我对自己的智力开始表示怀疑了,即使是对着手册一步一步做,为什么还要折腾4个多小时嘞?!

(1)配置源
deb     http://deb.torproject.org/torproject.org <DISTRIBUTION> main
用的是jauty

(2)添加源的认证签名
gpg --keyserver keys.gnupg.net --recv 886DDD89
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -
apt-get update
apt-get install tor
(3)配置privoxy(tor依赖安装的)

$sudo gedit /etc/privoxy/config 

forward-socks4a / 127.0.0.1:9050 . (可以在默认里找到这行,去掉注释)

Privoxy 会记录所有使用它浏览网页而留下的日志文件,如果你不需要保存日志文件,在下面这两行之前输入 “#” 注释掉。
#logfile logfile

#jarfile jarfile

(6)安装vidalia

sudo apt-get install vidalia
 
(5) 为firefox安装Torbutton 插件