1、CString:动态的TCHAR数组。它是一个完全独立的类,封装了+等操作符和字符串操作方法。
2、BSTR:专有格式的字符串(需要使用系统函数来操纵)。定义为:typedef OLECHAR FAR* BSTR
3、LPCTSTR:常量的TCHAR指针。定义为:typedef const char* LPCTSTR
二、要点
1、char*:指向ANSI字符数组的指针,其中每个字符占8位(有效数据是除掉最高位的其他七位),它保持
了与传统C/C++的兼容。
2、LPSTR:指向一个以“\0”结尾的ANSI字符数组的指针,可与char*互换使用,它通常在Win32中使用。
其中LP表示长指针(long pointer)。
3、LPCSTR:该数据类型的特性在于它的实例不能被使用它的API函数改变,除此之外与LPSTR等同。其
中C表示常量(CONSTANT)。
4、在Win16下长指针(LP)和短指针(P)有区别,而在Win32下它们是没有区别的,都是32位。
5、TCHAR在采用Unicode方式下编译时为wchar_t,在普通编码方式下编译时位char。
三、Unicode标准
1、为了满足程序代码国际化的需要,业界推出了Unicode标准,它提供了一种简单和一致的表示字符串的
方法,所有字符中的字节都是16位(两个字节)的值,其数量也可以满足几乎世界上所有书面语言字符的
编码需求,开发程序时使用Unicode(类型位wchar_t)是一种被鼓励的做法。
2、LPWSTR和LPCWSTR由此产生,它们的含义类似于LPSTR和LPCSTR,不同的是字符数据wchar_t为
16位,而char却为8位。
四、TCHAR数据类型
1、如果定义了_UNICODE,则声明如下:
2、如果没有定义_UNICODE,则声明如下:
式。而且CString是一个封装好了的类,更是大大地方便了用户的使用。
五、VC++中常用数据类型之间的转换
1、定义
2、其他数据类型到字符串的转换
(1)短整形int->字符串
(2)长整形long->字符串
3、从其他包含了字符串的变量中获取指向该字符串的指针
(1)从CString变量中获取字符串
(2)从BSTR类型的_varitant_t变量中获取字符串
4、字符串转换为其他数据类型
(1)i=atoi(temp); //字符串->短整型int
(2)l=atol(temp); //字符串->长整形long
(3)d=atof(temp); //字符串->浮点型double
5、其他数据类型转换到CString
(1)使用CString的成员函数Format来转换
A:str.Format("%d",i); //短整型int->CString
B:str.Format("%f",f); //浮点数float->CString
(2)支持CString构造函数的数据类型可以直接赋值,例如char*
六、BSTR、_bstr_t和CCombBSTR
1、char*->BSTR的转换
2、BSTR->char*的转换
七、VARIANT、_variant_t和COleVariant
1、对于VARIANT变量的赋值:首先给vt成员赋值,指明数据类型。再对联合结构中相同数据类型的变量赋
值(可参考VC98\Inlude\OAIDL.H头文件中关于tagVARIANT结构体的定义)。举例如下:
2、对于不马上赋值的VARIANT,最好先使用void VariantInit(VARIANTARG FAR* pvarg)函数对其进行初始
化,其本质是将vt设置为VT_EMPTY。vt与常用数据类型的对应关系(略)。
3、_variant_t是VARIANT的封装类,赋值可以使用强制类型转换,其构造函数会自动处理这些数据类型。
例如:
4、COleVariant与_variant_t的使用方法基本一样,示例如下:
八、其他
1、对消息的处理中,我们通常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据
(WORD),例如:
2、对于16位的数据(WORD),我们可以使用同样的方法分解成高低两个8位的数据(BYTE),例如:
3、如何将CString类型的变量赋给char*类型的变量
(1)CString::GetBuffer函数
(2)strcpy函数
ReleaseBuffer()函数来释放这个字符数组。
(3)memcpy函数
(4)LPCTSTR强制类型转换(不建议使用)
(5)CString->LPTSTR->char*
4、如何将CString类型的变量赋给const char*类型的变量
5、如何将CString类型的变量赋给LPCTSTR类型的变量
6、如何将LPCTSTR类型的变量赋给CString类型的变量
7、如何将char*类型的变量赋给CString类型的变量
(1)直接赋值:CString myString="This is a test";
(2)构造函数:CString s1("Tom");
8、如何将CString类型的变量赋给char[](字符串)类型的变量
(1)sprintf函数
还是常量。LPCTSTR表示const char*,它得到的字符串是不可写的!如果将其强制转换位LPTSTR(去
掉const),是极为危险的!要得到char*,应该使用GetBuffer或GetBufferSetLength函数,用完之后再调
用ReleaseBuffer函数。
(2)strcpy函数
九、关于CString的使用
1、指定CString形参
(1)对于大多数需要字符串参数的函数,最好将函数原型中的形参指定为一个指向字符(LPCTSTR),而非
CString的const指针。当将形参指定为指向字符的const指针时,可将指针传递到TCHAR数组(如字符串["hi
here"]或传递到CString对象)。CString对象将自动转换成LPCTSTR。任何能够使用LPCTSTR的地方也能
够使用CString对象。
(2)如果某个形参将不会被修改,则也将该参数指定为常量字符串引用(const CString&)。如果函数要修改
该字符串,则删除const修饰符。如果需要默认为空值,则将其初始化为空字符串([""]),如下所示:
(3)对于大多数函数的结果,按值返回CString对象即可。
2、串的基本运算
(1)求串长
(2)串复制
(3)串联接
(4)串比较
(5)字符定位
(6)注意
A:上述操作是最基本的,其中后4个操作还有变种形式:strncpy、strncath和strnchr。
B:其他的串操作见C的<string.h>头文件。在不同的高级语言中,对串运算的种类及符号都不尽相同。
C:其余的串操作一般可由这些基本操作组合而成。
标签: 杂谈 | 分类: C和Cplusplus |
L表示long指针
这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32为操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。
P表示这是一个指针
C表示是一个常量
T表示在Win32环境中, 有一个_T宏
这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。
STR表示这个变量是一个字符串
所以LPCTSTR就表示一个指向常固定地址的可以根据一些宏定义改变语义的字符串。
同样, LPCSTR就只能是一个ANSI字符串,在程序中我们大部分时间要使用带T的类型定义。
LPCTSTR == const TCHAR *
CString 和 LPCTSTR 可以说通用。原因在于CString定义的自动类型转换,没什么奇特的,最简单的C++操作符重载而已。
常量字符串ansi和unicode的区分是由宏_T来决定的。但是用_T("abcd")时,字符串"abcd"就会根据编译时的是否定一_UNICODE来决定是char* 还是 w_char*。 同样,TCHAR 也是相同目的字符宏。 看看定义就明白了。简单起见,下面只介绍 ansi 的情况,unicode 可以类推。
ansi情况下,LPCTSTR 就是 const char*, 是常量字符串(不能修改的)。
而LPTSTR 就是 char*, 即普通字符串(非常量,可修改的)。
这两种都是基本类型, 而CString 是 C++类,兼容这两种基本类型是最起码的任务了。
由于const char* 最简单(常量,不涉及内存变更,操作迅速), CString 直接定义了一个类型转换函数
operator LPCTSTR() {......},直接返回他所维护的字符串。
当你需要一个const char* 而传入了CString时, C++编译器自动调用 CString重载的操作符 LPCTSTR()来进行隐式的类型转换。
当需要CString , 而传入了 const char* 时(其实 char* 也可以),C++编译器则自动调用CString的构造函数来构造临时的 CString对象。
因此CString 和 LPCTSTR 基本可以通用。
但是 LPTSTR又不同了,他是 char*,意味着你随时可能修改里面的数据,这就需要内存管理了(如字符串变长,原来的存贮空间就不够了,则需要重新调整分配内存)。
所以 不能随便的将 const char* 强制转换成 char* 使用。
楼主举的例子
LPSTR lpstr = (LPSTR)(LPCTSTR)string;
就是这种不安全的使用方法。
这个地方使用的是强制类型转换,你都强制转换了,C++编译器当然不会拒绝你,但同时他也认为你确实知道自己要做的是什么。因此是不会给出警告的。
强制的任意类型转换是C(++)的一项强大之处,但也是一大弊端。这一问题在 vc6 以后的版本(仅针对vc而言)中得到逐步的改进(你需要更明确的类型转换声明)。
其实在很多地方都可以看到类似
LPSTR lpstr = (LPSTR)(LPCTSTR)string;
地用法,这种情况一般是函数的约束定义不够完善的原因,比如一个函数接受一个字符串参数的输入,里面对该字符串又没有任何的修改,那么该参数就应该定义成 const char*,但是很多初学者弄不清const地用法,或者是懒, 总之就是随意写成了 char* 。这样子传入CString时就需要强制的转换一下。
这种做法是不安全的,也是不被建议的用法,你必须完全明白、确认该字符串没有被修改。
CString 转换到 LPTSTR (char*), 预定的做法是调用CString的GetBuffer函数,使用完毕之后一般都要再调用ReleaseBuffer函数来确认修改 (某些情况下也有不调用ReleaseBuffer的,同样你需要非常明确为什么这么做时才能这样子处理,一般应用环境可以不考虑这种情况)。
同时需要注意的是, 在GetBuffer 和 ReleaseBuffer之间,CString分配了内存交由你来处理,因此不能再调用其他的CString函数。
CString 转LPCTSTR:
CString cStr;
const char *lpctStr=(LPCTSTR)cStr;
LPCTSTR转CString:
LPCTSTR lpctStr;
CString cStr=lpctStr;
------------------------------------------------
CString类功能强大,比STL的string类有过之无不及.新手使用CString时,都会被它强大
的功能所吸引.然而由于对它内部机制的不了解,新手在将CString向C的字符数组转换时
容易出现很多问题.因为CString已经重载了LPCTSTR运算符,所以CString类向const
char *转换时没有什么麻烦,如下所示:
char a[100];
CString str("aaaaaa");
strncpy(a,(LPCTSTR)str,sizeof(a));
或者如下:
strncpy(a,str,sizeof(a));
以上两种用法都是正确地.因为strncpy的第二个参数类型为const char *.所以编译器
会自动将CString类转换成const char *.很多人对LPCTSTR是什么东西迷惑不解,让我们
来看看:
1.LP表示长指针,在win16下有长指针(LP)和短指针(P)的区别,而在win32下是没有区别
的,都是32位.所以这里的LP和P是等价的.
2.C表示const
3.T是什么东西呢,我们知道TCHAR在采用UNICODE方式编译时是wchar_t,在普通时编译成char
那么就可以看出LPCTSTR(PCTSTR)在UINCODE时是const wchar_t *,PCWSTR,LPCWSTR,在
多字节字符模式时是const char *,PCSTR,LPCSTR.
接下来我们看在非UNICODE情况下,怎样将CString转换成char *,很多初学者都为了方便
采用如下方法:
(char *)(LPCSTR)str.这样对吗?我们首先来看一个例子:
strcpy((char *)(LPCTSTR)str,"aaaaaaaa");
配的字符缓冲区.如果往里面写的字符数超出了缓冲区范围,当然会出现异常.但这个程
序在Release版本下不会出现问题.原来对CString类已经进行了优化.当需要分配的内存
小于64字节时,直接分配64字节的内存,以此类推,一般CString类字符缓冲区的大小为
64,128,256,512...这样是为了减少内存分配的次数,提高速度.
那有人就说我往里面写的字符数不超过它原来的字符数,不就不会出错了,比如
CString str("aaaaaaa");
strcpy((char *)(LPCTSTR)str,"aa");
cout<<(LPCTSTR)str<这样看起来是没什么问题.我们再来看下面这个例子:
CString str("aaaaaaa");
strcpy((char *)(LPCTSTR)str,"aa");
cout<<(LPCTSTR)str<cout<我们看到str的长度没有随之改变,继续为7而不是2.还有更严重的问题:
str1里面的字符指针指向的缓冲区是一个.我们在Effective C++里面得知,如果你的类
内部有包含指针,请为你的类写一个拷贝构造函数和赋值运算符.不要让两个对象内部的
指针指向同一区域,而应该重新分配内存.难道是微软犯了错?
原来这里还有一个"写时复制"和"引用计数"的概念.CString类的用途很广,这样有可能
在系统内部产生大量的CString临时对象.这时为了优化效率,就采用在系统软件内部广
泛使用的"写时复制"概念.即当从一个CString产生另一个CString并不复制它的字符缓
冲区内容,而只是将字符缓冲区的"引用计数"加1.当需要改写字符缓冲区内的内容时,才
分配内存,并复制内容.以后我会给出一个"写时复制"和"引用计数"的例子
我们回到主题上来,当我们需要将CString转换成char *时,我们应该怎么做呢?其时只是
麻烦一点,如下所示:
CString str("aaaaaaa");
strcpy(str.GetBuffer(10),"aa");
str.ReleaseBuffer();
当我们需要字符数组时调用GetBuffer(int n),其中n为我们需要的字符数组的长度.使
用完成后一定要马上调用ReleaseBuffer();
还有很重要的一点就是,在能使用const char *的地方,就不要使用char *
/
对于大多数需要字符串参数的函数,最好将函数原型中的形参指定为一个指向字符 (LPCTSTR) 而非 CString 的 const 指针。当将形参指定为指向字符的 const 指针时,可将指针传递到 TCHAR 数组(如字符串 ["hi there"])或传递到 CString 对象。CString 对象将自动转换成 LPCTSTR。任何能够使用 LPCTSTR 的地方也能够使用 CString 对象。
如果某个形参将不会被修改,则也将该参数指定为常数字符串引用(即 const CString&)。如果函数要修改该字符串,则删除 const 修饰符。如果需要默认为空值,则将其初始化为空字符串 [""],如下所示:
void AddCustomer( const CString& name, const CString& address, const CString& comment = "" );
对于大多数函数结果,按值返回 CString 对象即可。