2011年1月30日
STLport:
下载:ww.stlport.org download STLport-5.2.1.tar.bz2;
编译:
1. 将STLport减压到d盘,d:\STLport;
2. 打开VS2005的命令行窗口,进入stl目录d:\STLport;
3. 执行命令configure msvc8 配置编译环境;
4. 打开目录d:\STLport\build\lib;
5. 执行命令 nmake -f msvc.mak clean install;
6. 编译完成后,*.dll,*.lib文件将被自动复制到STLport\bin和STLport\lib目录下。
debug模式下使用STLport,需定义宏__STL_DEBUG
debug模式下与boost配合使用,需定义宏__STLP_DEBUG
与MFC配合使用,需定义宏__STLP_USE_MFC
2010年12月9日
Qt中translate、tr关系 与中文问题
2010-09-22 00:15
题外话:何时使用 tr ?
在论坛中漂,经常遇到有人遇到tr相关的问题。用tr的有两类人:
- (1)因为发现中文老出问题,然后搜索,发现很多人用tr,于是他也开始用tr
- (2)另一类人,确实是出于国际化的需要,将需要在界面上显示的文件都用tr包起来,这有分两种:
- (2a) 用tr包住英文(最最推荐的用法,源码英文,然后提供英文到其他语言的翻译包)
- (2b) 用tr包住中文(源码用中文,然后提供中文到其他语言的翻译包)
注意哦,如果你正在用tr包裹中文字符,却不属于(2b),那么,这是个信号:
- 你在误用tr
- 你需要的是QString,而不是tr
tr 是做什么的?下面二者的区别是什么?
QString text1 = QObject::tr("hello"); QString text2 = QString("hello");
tr是用来实现国际化,如果你为这个程序提供了中文翻译包(其中hello被翻译成中文"你好"),那么text1的内容将是中文"你好";如果你为程序提供且使用日文翻译包,那么text1的内容将是日文。
tr是经过多级函数调用才实现了翻译操作,是有代价的,所以不该用的时候最好不要用。
关注的对象
本文关注的是tr或translate中包含中文字符串的情况:
这个问题本多少可说的。因为涉及到的编码问题和QString 与中文问题中是完全一样的,只不过一个是用的setCodecForCStrings一个用的是setCodecForTr。
简单回顾QString的中文问题
例子:
QString s1 = "我是中文"; QString s2("我是中文"); QString s3; s3 = "我是中文"
如果不指定编码,s1,s2,s3将全部都是(国内大多数人所称的)乱码。因为QString将这些const char *按照latin1来解释的,而不是用户期待的gbk或utf8。
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"))
这两条语句中的一条可以解决问题,至于如何选择,此处不再重复。
QObject::tr
说实话,在tr中使用中文不是个好主意。不过既然总有人用(无论是(1)还是(2b)),而且总有人遇到问题,所以还是简单整理一下吧。
相比QCoreApplication::translate,大家用tr应该用的很多了,尽管不少人不清楚tr究竟是做什么的^_^
这调用的是下面这个函数(至少我们可这么认为是)。
QString QObject::tr ( const char * sourceText, const char * disambiguation = 0, int n = -1 )
与QString("我是中文")完全一样,你必须告诉tr这个窄字符串是何种编码?你不告诉它,它就用latin1。于是所谓的乱码问题就出来了。
如何告诉tr你写的这几个汉字在磁盘中保存的是何种编码呢?这正是
QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312")); QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
所做的。这两个选择的原则,由于和前文完全一样,此处也不再重复。
如果你的编码采用的utf8,可以直接使用trUtf8而不必设置setCodecForTr。
如果你只关心乱码问题,到此为止就可以了(下面不再关注编码)。如果想对tr进一步了解,不妨。。继续。。
QCoreApplication::translate
我们知道tr是用于实现程序的国际化(或者说多语言翻译),看Qt相关资料的话,我们知道实现该功能的还有下面这个函数:
QString QCoreApplication::translate ( const char * context, const char * sourceText, const char * disambiguation, Encoding encoding, int n )
其实,这个才是真正进行翻译操作的函数,前面我们提到的tr最终是通过调用该函数来实现翻译功能的(稍后我们会看tr是如何调用translate的)。
对tr和这个函数,manual中都有比较详尽的解释。我们这儿简单看一下它的这几个参数:
- context 上下文,一般就是需要翻译的字符串所在的类的名字
- sourceText 需要翻译的字符串。(我们关注的编码其实就是它的编码)
- disambiguation 消除歧义用的。(比如我们的类内出现两处"close",一处含义是关闭,另一处含义是亲密的。显然需要让翻译人员知道这点区别)
- encoding 指定编码。它有两个值
- n 处理单复数(对中文来说,不存在这个问题)
tr与translate
这两个函数的说明,一个在QObject的manual,另一个在QCoreApplication的manual中。
介绍一下tr与translate的关系。前面提到了,tr调用的是translate。如果仅仅这样一说,没有证据,还真难以让大家相信。好吧,继续
tr 在何处定义
你可能说:这不废话吗,manual中写得明白的,它是QObject的静态成员函数。而且还有源码为证:
#ifdef qdoc static QString tr(const char *sourceText, const char *comment = 0, int n = -1); static QString trUtf8(const char *sourceText, const char *comment = 0, int n = -1); #endif
嘿嘿,差点就被骗了,发现没:它们被预处理语句包住了。
这说明了什么呢?说明了这段代码仅仅是用来生成Qt那漂亮的文档的(qdoc3从代码中抽取信息,生成一系列的html格式的manual)。
啊,也就是说,这是假的。那么真正的定义呢??在一个大家都很熟悉的地方,猜猜看?
这就是
Q_OBJECT
该宏的定义在src/corelib/kernel/qobjectdefs.h中
#define Q_OBJECT \ public: \ Q_OBJECT_CHECK \ static const QMetaObject staticMetaObject; \ Q_OBJECT_GETSTATICMETAOBJECT \ virtual const QMetaObject *metaObject() const; \ virtual void *qt_metacast(const char *); \ QT_TR_FUNCTIONS \ virtual int qt_metacall(QMetaObject::Call, int, void **); \ private:
其中的宏QT_TR_FUNCTIONS
# define QT_TR_FUNCTIONS \ static inline QString tr(const char *s, const char *c = 0) \ { return staticMetaObject.tr(s, c); } \ static inline QString trUtf8(const char *s, const char *c = 0) \ { return staticMetaObject.trUtf8(s, c); } \ static inline QString tr(const char *s, const char *c, int n) \ { return staticMetaObject.tr(s, c, n); } \ static inline QString trUtf8(const char *s, const char *c, int n) \ { return staticMetaObject.trUtf8(s, c, n); }
现在看到:tr调用的是 staticMetaObject对象的tr函数,staticMetaObject 的定义在moc生成的 xxx.moc 或 moc_xxx.cpp 文件内(你随时可以验证的)。
staticMetaObject 是一个 QMetaObject 类的实例,我们继续看一下该类的源码:
QString QMetaObject::tr(const char *s, const char *c) const { return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::CodecForTr); } QString QMetaObject::trUtf8(const char *s, const char *c) const { return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8); }
至此,我们应该都看清楚了。我们的 Q_OBJECT 宏展开后为生成 tr ,tr调用QCoreApplication的translate函数。而该函数需要指定编码。
|
2009年3月8日
调用DLL有两种方法:静态调用和动态调用.
(一).静态调用其步骤如下:
1.把你的youApp.DLL拷到你目标工程(需调用youApp.DLL的工程)的Debug目录下;
2.把你的youApp.lib拷到你目标工程(需调用youApp.DLL的工程)目录下;
3.把你的youApp.h(包含输出函数的定义)拷到你目标工程(需调用youApp.DLL的工程)目
录下;
4.打开你的目标工程选中工程,选择Visual C++的Project主菜单的Settings菜单;
5.执行第4步后,VC将会弹出一个对话框,在对话框的多页显示控件中选择Link页。然
后在Object/library modules输入框中输入:youApp.lib
6.选择你的目标工程Head Files加入:youApp.h文件;
7.最后在你目标工程(*.cpp,需要调用DLL中的函数)中包含你的:#include "youApp.h"
注:youApp是你DLL的工程名。
2.动态调用其程序如下:
动态调用时只需做静态调用步骤1.
{
HINSTANCE hDllInst = LoadLibrary("youApp.DLL");
if(hDllInst)
{
typedef DWORD (WINAPI *MYFUNC)(DWORD,DWORD);
MYFUNC youFuntionNameAlias = NULL; // youFuntionNameAlias 函数别名
youFuntionNameAlias = (MYFUNC)GetProcAddress
(hDllInst,"youFuntionName");
// youFuntionName 在DLL中声明的函数名
if(youFuntionNameAlias)
{
youFuntionNameAlias(param1,param2);
}
FreeLibrary(hDllInst);
}
}
显式(静态)调用:
LIB + DLL + .H,注意.H中dllexport改为dllimport
隐式(动态)调用:
DLL + 函数原型声明,先LoadLibrary,再GetProcAddress(即找到DLL中函数的地址),不用后FreeLibrary
2009年2月16日
面向对象的三个基本特征(讲解)
面向对象的三个基本特征是:封装、继承、多态。
封装
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
Ø 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
实现多态,有二种方式,覆盖,重载。
覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
概念讲解
泛化(Generalization)
图表 1 泛化
在上图中,空心的三角表示继承关系(类继承),在UML的术语中,这种关系被称为泛化(Generalization)。Person(人)是基类,Teacher(教师)、Student(学生)、Guest(来宾)是子类。
若在逻辑上B是A的“一种”,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性。
例如,教师是人,Teacher 是Person的“一种”(a kind of )。那么类Teacher可以从类Person派生(继承)。
如果A是基类,B是A的派生类,那么B将继承A的数据和函数。
如果类A和类B毫不相关,不可以为了使B的功能更多些而让B继承A的功能和属性。
若在逻辑上B是A的“一种”(a kind of ),则允许B继承A的功能和属性。
聚合(组合)
图表 2 组合
若在逻辑上A是B的“一部分”(a part of),则不允许B从A派生,而是要用A和其它东西组合出B。
例如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生(继承)而成。
聚合的类型分为无、共享(聚合)、复合(组合)三类。
聚合(aggregation)
图表 3 共享
上面图中,有一个菱形(空心)表示聚合(aggregation)(聚合类型为共享),聚合的意义表示has-a关系。聚合是一种相对松散的关系,聚合类B不需要对被聚合的类A负责。
组合(composition)
图表 4 复合
这幅图与上面的唯一区别是菱形为实心的,它代表了一种更为坚固的关系——组合(composition)(聚合类型为复合)。组合表示的关系也是has-a,不过在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。
依赖(Dependency)
图表 5 依赖
这里B与A的关系只是一种依赖(Dependency)关系,这种关系表明,如果类A被修改,那么类B会受到影响。
2009年2月9日
[分享]VC.net 2005找不到MSVCR80D.dll的完美解决方案
stevenz - BY - 2007-8-8 16:04:44
问题描述:大部分的vs.net 2005的用户在新建“win32项目-windows应用程序”的时候,新建的工程都通不过去,出现如下提示:
Solution to “MSVCR80D.dll not found”.
“没有找到MSVCR80D.dll,因此这个应用程序未能启动。重新安装应用程序可能会修复此问题。”
问题所在:由于vs.net 2005 采用了一种新的DLL方案,搞成一个exe还要配有一个manifest文件(一般在嵌入文件里了,所以看不到,不过也可以不嵌入,这样会生产一个<程序名>.exe.manifest的文件,没它exe自己就转不了了:)这是个新功能,微软弄了个新工具(mt.exe),结果不好用,好像是fat32下时间戳有问题(在ntfs下这个问题就没有了),搞得manifest有时嵌入不到exe中(默认配置是嵌入的,所以就报错找不到dll了。
解决方案:
1. 微软对于这个问题应该也有处理,不过感觉不是很人性化。在“属性->配置属性->清单工具->常规“下有一个”使用FAT32解决办法,把它选成是,就可以了。(注意:一定要先配置这个选项,然后再编译工程,要不然还是不好用:)
2. 找到你的工程的文件夹,如(myproject),找到其下的myproject\myproject\Debug\ myproject.rec,把它删掉(删掉整个Debug目录也可以),重新编译,搞定!
3. 本解决方案可以直接再应用向导中配置,严重符合高级人机界面要求:
1) 首先找到你的vs.net安装目录(如我的是E:\Program Files\Microsoft Visual Studio 8),定位到Microsoft Visual Studio 8\VC\VCWizards\AppWiz\Generic\Application文件夹,备份这个Application文件夹,不然一会你自己改咂了我可不管啊:)。
2) 打开html\2052,看到两个文件了吧,就那个AppSettings.htm了,这个管着你的那个配置向导的界面,用UE(不要告诉我你不知道ue啥东西,baidu it)打开,在266行“ </SPAN>”后回车,然后插入一下内容:
<!-- this (hua)section is added by HUA. -->
<BR><BR><BR><BR><BR>
<SPAN class='itemTextTop' id='FILE_SYSTEM_SPAN' title=''>选择你所使用的文件系统:
<P CLASS='Spacer'> </P>
<INPUT TYPE='radio' CLASS='Radio' checked onPropertyChange='' NAME='filesystem' ID='FAT32' ACCESSKEY='F' TITLE='FAT32'>
<DIV CLASS='itemTextRadioB' ID='FAT32_DIV' TITLE='FAT32'>
<LABEL FOR='FAT32' ID='FAT32_LABEL'>FAT32(<U>F</U>)</LABEL>
</DIV>
<BR>
<INPUT TYPE='radio' CLASS='Radio' onPropertyChange='' NAME='filesystem' ID='NTFS' ACCESSKEY='N' TITLE='NTFS'>
<DIV CLASS='itemTextRadioB' ID='NTFS_DIV' TITLE='NTFS'>
<LABEL FOR='NTFS' ID='NTFS_LABEL'>NTFS(<U>N</U>)</LABEL>
</DIV>
</SPAN>
<!-- end of (hua)section -->
好,保存关闭,这个改完了,准备下一个。
3) 打开scripts\2052,这里就一个文件,ue打开它,找到138行“ var bATL = wizard.FindSymbol('SUPPORT_ATL');”其后回车,插入如下内容:
// this (hua)section is added by HUA.
var bNTFS = wizard.FindSymbol('FILE_SYSTEM_SPAN');
// end of (hua)section
好,继续找到210行(源文件的210,你加了上边的语句就不是210了:)“ config = proj.Object.Configurations.Item('Release');”注意这次要在这行“前边”加如下内容:
// this (hua)section is added by HUA.
if(!bNTFS)
{
var MFTool = config.Tools('VCManifestTool');
MFTool.UseFAT32Workaround = true;
}
// end of (hua)section
该段内容解决了在向导下不点“下一步”而直接点“完成”来生成一个win32控制台项目时,则未能自动设置UseFAT32Workaround属性的值为true。
修改后新建win32项目时的向导如图
2009年2月8日
IPv4中使用gethostbyname()函数完成主机名到地址解析,但是该API不允许调用者指定所需地址类型的任何信息,返回的结构只包含了用于存储IPv4地址的空间。为了解决该问题,IPv6中引入了getaddrinfo()的新API,它是协议无关的,既可用于IPv4也可用于IPv6。调用该函数会获得一个addrinfo结构的列表,调用的返回值是addrinfo的结构(列表)指针。
本文结合在WinowsXP和Windows2003 Server上使用该函数的经验,对getaddrinfo函数和addrinfo数据结构进行介绍,并对其参数的设置加以讨论,主要包括nodename和servname的取值对返回值的影响,hints成员变量的设置对返回值的影响等。
可能有不完全或不准确的地方,欢迎大家讨论并指出。
1.getaddrinfo函数原型
函数
|
参数说明
|
int getaddrinfo(
const char* nodename
const char* servname,
const struct addrinfo* hints,//
struct addrinfo** res
);
|
nodename:节点名可以是主机名,也可以是数字地址。(IPV4的10进点分,或是IPV6的16进制)
servname:包含十进制数的端口号或服务名如(ftp,http)
hints:是一个空指针或指向一个addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索。
res:存放返回addrinfo结构链表的指针
|
Getaddrinfo提供独立于协议的名称解析。
函数的前两个参数分别是节点名和服务名。节点名可以是主机名,也可以是地址串(IPv4的点分十进制数表示或IPv6的十六进制数字串)。服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等。注意:其中节点名和服务名都是可选项,即节点名或服务名可以为NULL,此时调用的结果将取缺省设置,后面将详细讨论。
函数的第三个参数hints是addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索。函数的返回值是一个指向addrinfo结构的链表指针res。
2.addrinfo结构
结构
|
固定的参数
|
typedef struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char* ai_canonname;
struct sockaddr* ai_addr;
struct addrinfo* ai_next;
}
|
ai_addrlen must be zero or a null pointer
ai_canonname must be zero or a null pointer
ai_addr must be zero or a null pointer
ai_next must be zero or a null pointer
|
可以改动的参数
|
ai_flags:AI_PASSIVE,AI_CANONNAME,AI_NUMERICHOST
ai_family: AF_INET,AF_INET6
ai_socktype:SOCK_STREAM,SOCK_DGRAM
ai_protocol:IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc.
|
3 参数说明
在getaddrinfo函数之前通常需要对以下6个参数进行以下设置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol
在6项参数中,对函数影响最大的是nodename,sername和hints.ai_flag
而ai_family只是有地址为v4地址或v6地址的区别。而ai_protocol一般是为0不作改动。
其中ai_flags、ai_family、ai_socktype说明如下:
参数
|
取值
|
值
|
说明
|
ai_family
|
AF_INET
|
2
|
IPv4
|
AF_INET6
|
23
|
IPv6
|
AF_UNSPEC
|
0
|
协议无关
|
ai_protocol
|
IPPROTO_IP
|
0
|
IP协议
|
IPPROTO_IPV4
|
4
|
IPv4
|
IPPROTO_IPV6
|
41
|
IPv6
|
IPPROTO_UDP
|
17
|
UDP
|
IPPROTO_TCP
|
6
|
TCP
|
ai_socktype
|
SOCK_STREAM
|
1
|
流
|
SOCK_DGRAM
|
2
|
数据报
|
ai_flags
|
AI_PASSIVE
|
1
|
被动的,用于bind,通常用于server socket
|
AI_CANONNAME
|
2
|
|
AI_NUMERICHOST
|
4
|
地址为数字串
|
对于ai_flags值的说明:
AI_NUMERICHOST
|
AI_CANONNAME
|
AI_PASSIVE
|
0/1
|
0/1
|
0/1
|
如上表所示,ai_flagsde值范围为0~7,取决于程序如何设置3个标志位,比如设置ai_flags为 “AI_PASSIVE|AI_CANONNAME”,ai_flags值就为3。三个参数的含义分别为:
(1)AI_PASSIVE当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。
当节点名位NULL,且此标志置位,则返回的地址将是通配地址。
如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。
(2)AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
(3)AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。
4.实际使用的几种常用设置
一般情况下,client/server编程中,server端调用bind(如果面向连接的还需要listen),client则不用掉bind函数,解析地址后直接connect(面向连接)或直接发送数据(无连接)。因此,比较常见的情况有
(1) 通常服务器端在调用getaddrinfo之前,ai_flags设置AI_PASSIVE,用于bind;主机名nodename通常会设置为NULL,返回通配地址[::]。
(2) 客户端调用getaddrinfo时,ai_flags一般不设置AI_PASSIVE,但是主机名nodename和服务名servname(更愿意称之为端口)则应该不为空。
(3) 当然,即使不设置AI_PASSIVE,取出的地址也并非不可以被bind,很多程序中ai_flags直接设置为0,即3个标志位都不设置,这种情况下只要hostname和servname设置的没有问题就可以正确bind。
上述情况只是简单的client/server中的使用,但实际在使用getaddrinfo和参考国外开源代码的时候,曾遇到一些将servname(即端口)设为NULL的情况
(当然,此时nodename必不为NULL,否则调用getaddrinfo会报错)。以下分情况进行了测试:
(1) 如果nodename是字符串型的IPv6地址,bind的时候会分配临时端口;
(2) 如果nodename是本机名,servname为NULL,则根据操作系统的不同略有不同,本文仅在WinXP和Win2003上作了测试。
a) WinXP系统(SP2)返回loopback地址[::1]
b) Win2003则将本机的所有IPv6地址列表加以返回。因为通常一台IPv6主机都有可能不止一个IPv6地址,比如fe80::1(本机loopback地址)、fe80::***的Link-Local地址、3ffe:***的全局地址等等。这种情况下调用getaddrinfo会将这些地址全部返回,调用者应该注意如何使用这些地址。另外要注意的是,对于fe80::的地址在绑定的时候必须标明接口地址,即使用fe80::20d:60ff:fe78:51c2%4或fe80::1%1这样的地址格式,通过getaddrinfo直接取出fe80地址好像无法直接bind。
5.几句废话
在Windows环境调试IPv6的程序个人感觉还是使用WinXP(SP2)和Win2003基本上没有太大的区别,使用Win2003更规范一些。
用VC编写和调试IPv6的程序一定要安装Windows较新的SDK,我安装的是MS_Platform_SDK_Feb_2003,否则库函数和头文件可能都会有问题。
[参考]
[1]MSDN Library – January 2004
ms-help://MS.MSDNQTR.2004JAN.1033/winsock/winsock/getaddrinfo_2.htm
ms-help://MS.MSDNQTR.2004JAN.1033/winsock/winsock/addrinfo_2.htm
[2]《理解IPv6》("Understanding IPv6”),清华大学出版社
2009年1月28日
解决方法: 1.在winpcap库的packet32.h文件里面加入代码(且位于“struct npf_if_addr”定义之前) #ifndef _SS_PAD1SIZE struct sockaddr_storage { u_char sa_len; u_char sa_family; u_char padding[128]; }; #endif 2.sockaddr_storage 改成 sockaddr
|
2008年6月21日
很多人遇到这个问题,终于找到了原因。
两行简单的代码:
CFileDialog dlg(true);
dlg.DoModal();
第一次随便选择一个文件,第二次选择桌面的一个.txt文件,当鼠标移动到这个txt文件的时候,程序就挂了。怀疑是 微软的问题?
换api操作,照样挂。
换记事本,挂。
Windbg跟踪,找不到哪个模块,程序最后崩溃在shell32.dll,检查进程的dll模块,最后终于找到是Adobe的pdfshell.dll引起的。删除掉或者regsvr32 /u 卸载就可以了。水平有限,不能跟进那个dll去检查了。
pdf版本7.0.8.0
系统xp sp2
2008年6月16日
A bit of fun: fun with bits
【原文见:http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=bitManipulation】
作者 By bmerry
TopCoder Member
Introduction
TopCoder比赛中的大部分优化都是高端优化;也就是说,它们影响的是算法而不是实现。尽管如此,使用位运算一个非常有用和高效的低端优化,或者使用一个整数的位来表示一个集合。使用它不仅可以在速度和空间上上产生一个巨大的提高,还能同时简化代码。
在进入更多高级技巧的讨论之前,我先对基本概念进行一个简要的分类。
The basics
位运算的核心是按位操作 &(and),|(or),~(not)和^(xor)。你应该熟悉前三者的bool运算形式(&&,||和!),下面是真值表:
这些按位运算操作的变形是一样的,除了在解释参数时不使用true或者false而是对参数的每一个位进行运算。因此,如果A是1010,B是1100,那么
A & B = 1000
A | B = 1110
A ^ B = 0110
~A = 11110101 (1的数目由A的类型决定).
另外两个我们需要的操作符是移位操作符 a << b 和a >> b。前者将a中所有的位向左移动b个位置;而后者向右进行移动。对于非负值(这是我们仅需要关注的),新出现的位使用0进行填充。你可以考虑将b左移b位看作乘以2^b,将右移看作整数处以2^b。移位经常被使用来访问一个特定的位,例如,1 << x是一个x位被设置,而其他位被清楚的二进制数字(位通常总是从最靠右边的/最低有效位进行计数,通常用0标注)
一般来说,我们使用一个整数来表示一个拥有32(或者64,使用一个64位的整数)个值的域的集合,用1来代表元素在集合中,而0表示不在其中。下面的操作都相当的直接,这里ALL_BITS是一个与域元素相对应的全1的数字。
集合并
A | B
集合交
A & B
集合差
A & ~B
集合补集
ALL_BITS ^ A
设置某个位
A |= 1 << bit
清除某个位
A &= ~(1 << bit)
测试某个位
(A & 1 << bit) != 0
Extracting every last bit
在这一小节中,我们考虑这样一个问题,找出一个数的最高和最低的1位。对于将一个集合分散成单个元素有一些基本的运算。
通过正确的位运算和算术运算,找出最低位可以非常简单。假设我们想要找到x的最低位(非0位)。如果我们从x中减1,那么这个位就清除了,但是x中其他所有的位都还存在。因此, x & ~(x-1)包含了x的最低设置位。尽管如此,我们得到的仅仅是值,而不是这个位的索引。
如果我们需要得到最高或者最低位的下标,最明显的方法是遍历位(向前或者向后)直到找到被设置的位。一眼撇过,这看起来很慢,因为它根本没用到位压缩。尽管如此,如果N个元素的域的2^N个子集都算起来,那么这个循环平均使用两个迭代,事实上这个是最快的方法。
386当中引入了用于位扫描的CPU指令: BSF(bit scan forward)和BSR(bit scan reverse)。GCC使用内建的__builtin_ctz (计数末尾的0)
和__builtin_clz (计数前导0)函数来完成这些操作。对于TopCoder中的C++程序员来说,有最方便的找到位索引的方法。警告:参数为0时返回值未定义。
最后,有一个可移植的在循环解决方案需要很多迭代的情况下运行很好的方法。使用每种类型的4个或者8个字节的整数来索引一张预先定义好的存储每一个字节里面最高(低)设置位的索引的256个条目的表。整数的最高(低)位就是表中条目的最大(最小)条目。这个方法仅仅是为了完整性而提及,在TopCoder比赛中它获得的效率不太可能得到证明。
Counting out the bits
可以很容易的判断一个数是不是2的幂次:清除最低的1位(见上面)并且检查结果是不是0.尽管如此,有的时候需要直到有多少个被设置了,这就相对有点难度了。
GCC有一个叫做__builtin_popcount的内建函数,它可以精确的计算1的个数。尽管如此,不同于__builtin_ctz,它并没有被翻译成一个硬件指令(至少在x86上不是)。相反的,它使用一张类似上面提到的基于表的方法来进行位搜索。这无疑很高效并且非常方便。
其他语言的使用者没有这个选项(尽管他们可以重新实现这个算法)。如果一个数只有很少的1的位,另外一个方法是重复的获取最低的1位,并且清除它。
All the subsets
位运算的一个大的优点在于它可以很平常的遍历一个N个元素集合的子集:每一个N位的值表示一些子集。更好一点是,如果A是B的子集,
那么表示A的数要比表示B的小,这对于一些动态规划的解决方案很方便。
它同样可以遍历一个特殊集合的子集(通过一些位串表示),例如让你以倒序进行访问(如果这个是一个问题的话,可以将它们生成的时
候放到列表中,然后从后向前访问)。这个技巧和找一个数字的最低位是类似的。如果我们从子集中减1,那么最低的集合元素被清除了,并且
每一个更小的元素被设置了。尽管如此,我们只想在超集中设置这些较小的元素。因此迭代步骤就是i = (i - 1) & superset。
Even a bit wrong scores zero
在进行位操作的时候容易犯一些错误。在你的代码中注意以下方面:
1. 当执行移位操作,例如a << b, x86架构仅使用b的底部5个位(对64位元素是6)。这就意味着左移(右)32位等于什么都没做,
而不是清除所有的位。这个行为在Java和C#语言当中也被指定了;C99中说到移位至少值的大小将得到未定义的结果。历史上:8086使用完全的
移位寄存器,行为上的改变通常用来检测新的处理器。
2. &和|的优先级比比较运算符要低。这就意味着x & 3 == 1被解释成了x & (3 == 1),这不是我们想要的。
3. 如果你想书写可移植的C/C++代码,确保使用无符号的类型,尤其当你想要使用最高位的时候。C99中谈到对一个负值进行移位操作
是未定义的。Java仅有有符号类型: >>将使用扩展类型值(这可能并不是我们想要的),但是Java指定的>>>操作符将使用0进行填充。
Cute tricks
还有一些可以使用微操作进行的技巧。它们可以用来让你的朋友感到惊讶,但是一般来说在实际中并不是值得使用。
将一个整数的位进行逆转
x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1);
x = ((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2);
x = ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4);
x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
x = ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16);
作为一个练习,看看你能否将它应用在取得一个字长里面所有的位的数量,遍历{0,1,...N-1}所有k个元素的子集
int s = (1 << k) - 1;
while (!(s & 1 << N))
{
// do stuff with s
int lo = s & ~(s - 1); // lowest one bit
int lz = (s + lo) & ~s; // lowest zero bit above lo
s |= lz; // add lz to the set
s &= ~(lz - 1); // reset bits below lz
s |= (lz / lo / 2) - 1; // put back right number of bits at end
}
在C中,最后移行可以写作 s |= (lz >> ffs(lo)) - 1来避免除法。
评估x ? y : -y,这里x是0或者1
(-x^y) + x
这个可以在二次补码记法的架构上工作(现在你能找到的机器几乎都是这样),这里负数通过将所有变反再加1得到。注意在i686和上面所说的,原始表达式是由于CMOVE(conditional move)指令而变得高效(也就是,没有分支)。
Sample problems
TCCC 2006, Round 1B Medium
对每一个城市,为它相邻的城市保持一个位集。一旦工厂的部分建筑被选中(递归的),将这些位加在一起将给出一个描述所有部分工厂集合的可能位置。如果这个位集合中拥有k个位,那么将有C(m,k)种方法分配集装工厂。
TCO 2006, Round 1 Easy
节点的数量暗示可以考虑所有可能的子集。对每一个可能的子集,我们考虑两种可能性:要么最小标号的节点根本不进行通讯,在这种情况下我们在原来的集合中排除它,要么它与一些节点交流,在这种情况下,我们将所有的这些节点排除。结果得到的代码相当短:
static int dp[1 << 18];
int SeparateConnections::howMany(vector <string> mat)
{
int N = mat.size();
int N2 = 1 << N;
dp[0] = 0;
for (int i = 1; i < N2; i++)
{
int bot = i & ~(i - 1);
int use = __builtin_ctz(bot);
dp[i] = dp[i ^ bot];
for (int j = use + 1; j < N; j++)
if ((i & (1 << j)) && mat[use][j] == 'Y')
dp[i] = max(dp[i], dp[i ^ bot ^ (1 << j)] + 2);
}
return dp[N2 - 1];
}
SRM 308, Division 1 Medium
棋盘上有36个正方形,并且都是不可辨别的,因此所有可能的位置可以编码成一个64位的整数。第一步是枚举所有合法的
移动,任何合法的移动可以使用3个位域表示:一个before状态,一个after状态和一个mask。mask定义了before状态的哪一个部分比较重要。
移动可以从当前的状态得到 if (current & mask) == before;如果得到了当前状态,那么新的状态是(current & ~mask) | after
SRM 320, Division 1 Hard
条件告诉我们最多8个列(如果有更多的话,我们可以交换行和列),因此考虑每种方法来放置行是可行的。一旦我们有了这些信息,我们
可以解决这个问题(参考match editorial详情)。我们因此需要一个n位的不具有两个相邻1的整数的列表,并且我们也需要知道在每一行当中
有多少个1位。我的代码如下:
for (int i = 0; i < (1 << n); i++)
{
if (i & (i << 1)) continue;
pg.push_back(i);
pgb.push_back(__builtin_popcount(i));
}