CString和char* 范例转化
当前位置:以往代写 > C/C++ 教程 >CString和char* 范例转化
2019-06-13

CString和char* 范例转化

CString和char* 范例转化

副标题#e#

CString 是一种很有用的数据范例。它们很洪流平上简化了MFC中的很多操纵,使得MFC在做字符串操纵的时候利便了许多。 不管奈何,利用CString有许多非凡的能力,出格是对付纯C配景下走出来的措施员来说有点难以进修。

1、CString 转化 成 char*(1) —— 强制范例转换为 LPCTSTR

这是一种略微硬性的转换,我们首先要相识 CString 是一种很非凡的 C++ 工具,它内里包括了三个值:一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。 有 效字符数的巨细可以是从0到该缓冲最大长度值减1之间的任何数(因为字符串末了有一个NULL字符)。字符记数缓和冲区长度被 巧妙埋没。

除非你做一些非凡的操纵,不然你不行能知道给CString工具分派的缓冲区的长度。这样,纵然你得到了该缓冲的 地点,你也无法变动个中的内容,不能截短字符串,也 绝对没有步伐加长它的内容,不然第一时间就会看到溢出。

LPCTSTR 操纵符(可能更明晰地说就是 TCHAR * 操纵符)在 CString 类中被重载了,该操纵符的界说是返回缓冲区的地点,因此,假如 你需要一个指向 CString 的 字符串指针的话,可以这样做:

CString s("GrayCat");

LPCTSTR p = s;

它可 以正确地运行。这是由C语言的强制范例转化法则实现的。当需要强制范例转化时,C++规测容许这种选择。好比,你可以将(浮 点数)界说为将某个复数 (有一对浮点数)举办强制范例转换后只返回该复数的第一个浮点数(也就是其实部)。可以象下面 这样:

Complex c(1.2f, 4.8f);

float realpart = c;

假如(float)操纵符界说正确的话,那么实部的的值应该是 1.2。

这种强制转化适合所有这种环境,譬喻,任何带有 LPCTSTR 范例参数的函数城市强制执行这种转换。 于是,你大概有 这样一个函数(也许在某个你买来的DLL中):

BOOL DoSomethingCool(LPCTSTR s);

你象下面这样挪用它:

CString file("c:\\myfiles\\coolstuff")

BOOL result = DoSomethingCool(file);

它能正确运行。因为 DoSomethingCool 函数已经说明白需要一个 LPCTSTR 范例 的参数,因此 LPCTSTR 被应用于该参数,在 MFC 中就是返回的串地点。

假如你要名目化字符串怎么办呢?

CString graycat("GrayCat");

CString s;

s.Format("Mew! I love %s", graycat);

注 意由于在可变参数列表中的值(在函数说明中是以“…”暗示的)并没有隐含一个强制范例转换操纵符。你会获得什么功效呢 ?

一个令人惊奇的功效,我们获得的实际功效串是:

"Mew! I love GrayCat"。

因为 MFC 的设计者们 在设计 CString 数据范例时很是小心, CString 范例表达式求值后指向了字符串,所以这里看不到任何象 Format 或 sprintf 中的强制范例转换,你仍然可以获得正确的行为。描写 CString 的附加数据实际上在 CString 名义地点之后。

有一件工作 你是不能做的,那就是修改字符串。好比,你大概会实验用“,”取代“.”(不要做这样的,假如你在乎国际化问题,你应该使 用十进制转换的 National Language Support 特性)下面是个简朴的例子:

CString v("1.00"); // 钱币金 额,两位小数

LPCTSTR p = v;

p[lstrlen(p) – 3] = ”,”;

这时编译器会报错,因为你赋值了一个 常量串。假如你做如下实验,编译器也会错:

strcat(p, "each");

因为 strcat 的第一个参数应该是 LPTSTR 范例的数据,而你却给了一个 LPCTSTR。

不要试图钻这个错误动静的牛角尖,这只会使你本身陷入贫苦!

原因是缓冲有一个计数,它是不行存取的(它位于 CString 地点之下的一个埋没区域),假如你改变这个串,缓冲中的 字符计数不会反应所做的修改。另外,假如字符串长度刚好是该字符串物理限制的长度,那么扩展该字符串将改写缓冲以外的任 何数据,那是你无权举办写操纵的内存,你会毁换坏不属于你的内存。这是应用措施真正的灭亡处方。


#p#副标题#e#

2、CString转化 成char* (2)—— 利用 CString 工具的 GetBuffer 要领

假如你需要修改 CString 中的内容,它有一个非凡的要领可 以利用,那就是 GetBuffer,它的浸染是返回一个可写的缓冲指针。 假如你只是规划修改字符可能截短字符串,你完全可以这 样做:

CString s(_T("File.ext"));

LPTSTR p = s.GetBuffer();

LPTSTR dot = strchr(p, ”.”); // OK, should have used s.Find…

if(p != NULL)

*p = _T(”\0”);

s.ReleaseBuffer();

这是 GetBuffer 的第一种用法,也是最简朴的一种,不消给它通报参数,它利用默认值 0,意思是 :“给我这个字符串的指针,我担保不加长它”。当你挪用 ReleaseBuffer 时,字符串的实际长度会被从头计较,然后存入 CString 工具中。

#p#分页标题#e#

必需强调一点,在 GetBuffer 和 ReleaseBuffer 之间这个范畴,必然不能利用你要操纵的这个缓冲的 CString 工具的任何要领。因为 ReleaseBuffer 被挪用之前,该 CString 工具的完整性得不到保障。研究以下代码:

CString s(…);

LPTSTR p = s.GetBuffer();

//… 这个指针 p 产生了许多工作

int n = s.GetLength(); // 有大概给堕落误的谜底

s.TrimRight(); // 不能担保能正常事情

s.ReleaseBuffer(); // 现 在应该 OK

int m = s.GetLength(); // 这个功效可以担保是正确的。

s.TrimRight(); // 将正常事情。

假设你想增加字符串的长度,你首先要知道这个字符串大概会有多长,比如是声明字符串数组的时候用:

char buffer [1024];

暗示 1024 个字符空间足以让你做任何想做得工作。在 CString 中与之意义相等的暗示法:

LPTSTR p = s.GetBuffer(1024);

挪用这个函数后,你不只得到了字符串缓冲区的指针,并且同时还得到了长度至少为 1024 个字符的空 间(留意,我说的是“字符”,而不是“字节”,因为 CString 是以隐含方法感知 Unicode 的)。

同时,还应该留意的是 ,假如你有一个常量串指针,这个串自己的值被存储在只读内存中,假如试图存储它,纵然你已经挪用了 GetBuffer ,并得到 一个只读内存的指针,存入操纵会失败,并陈诉存取错误。我没有在 CString 上证明这一点,但我看到过大把的 C 措施员常常 犯这个错误。

C 措施员有一个通病是分派一个牢靠长度的缓冲,对它举办 sprintf 操纵,然后将它赋值给一个 CString:

char buffer[256];

sprintf(buffer, "%……", args, …); // … 部门省略很多细节

CString s = buffer;

固然更好的形式可以这么做:

CString s;

s.Format(_T("%…."), args, …);

假如你的 字符串长度万一高出 256 个字符的时候,不会粉碎仓库。

别的一个常见的错误是:既然牢靠巨细的内存不事情,那么就 回收动态分派字节,这种做法漏洞更大:

int len = lstrlen(parm1) + 13   lstrlen(parm2) + 10 + 100;

char * buffer = new char[len];

sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);

CString s = buffer;

……

delete [] buffer;

它可以能被简朴地写成:

CString s;

s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);

需要留意 sprintf 例子都不是 Unicode 停当的,尽量你可以利用 tsprintf 以及用 _T() 来困绕名目化字符串,可是根基思路仍然是在 走弯路,这这样很容易堕落。

#p#副标题#e#

3、CString 和姑且工具

这是呈此刻 microsoft.public.vc.mfc 新闻组中的一个小 问题,我简朴的提一下,这个问题是有个措施员需要往注册表中写入一个字符串,他写道:

我试着用 RegSetValueEx() 配置 一个注册表键的值,可是它的功效老是令我狐疑。当我用char[]声明一个变量时它能正常事情,可是当我用 CString 的时候, 老是获得一些垃圾: "ÝÝÝÝ…ÝÝÝÝ&Ya cute;Ý"为了确认是不是我的 CString 数据出了问题,我试着用 GetBuffer,然后强制转化成 char*,LPCSTR 。GetBuffer 返回的值是正确的,可是当我把它赋值给 char* 时,它就酿成垃圾了。以下是我的措施段:

char* szName = GetName().GetBuffer(20);

RegSetValueEx(hKey, "Name", 0, REG_SZ,

(CONST BYTE *) szName,

strlen (szName + 1));

这个 Name 字符串的长度小于 20,所以我不认为是 GetBuffer 的参数的问题。

真让人狐疑,请帮帮 我。

亲爱的 Frustrated,

你犯了一个相当微妙的错误,智慧反被智慧误,正确的代码应该象下面这样:

CString Name = GetName();

RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,

(CONST BYTE *) (LPCTSTR)Name,

(Name.GetLength() + 1) * sizeof(TCHAR));

为什么我写的代码能行而你写的就有问题呢?主要是因 为当你挪用 GetName 时返回的 CString 工具是一个姑且工具。拜见:《C++ Reference manual》§12.2

在一些情况中,编 译器有须要建设一个姑且工具,这样引入姑且工具是依赖于实现的。假如编译器引入的这个姑且工具所属的类有结构函数的话, 编译器要确保这个类的结构函数被挪用。同样的,假如这个类声明有析构函数的话,也要担保这个姑且工具的析构函数被挪用。

编译器必需担保这个姑且工具被销毁了。被销毁简直切所在依赖于实现…..这个析构函数必需在退出建设该姑且工具的范畴 之前被挪用。

#p#分页标题#e#

大部门的编译器是这样设计的:在姑且工具被建设的代码的下一个执行步调处隐含挪用这个姑且工具的析构函 数,实现起来,一般都是在下一个分号处。因此,这个 CString 工具在 GetBuffer 挪用之后就被析构了(顺便提一句,你没有 来由给 GetBuffer 函数通报一个参数,并且没有利用ReleaseBuffer 也是差池的)。所以 GetBuffer 原来返回的是指向这个临 时工具中字符串的地点的指针,可是当这个姑且工具被析构后,这块内存就被释放了。然后 MFC 的调试内存分派器会从头为这 块内存全部填上 0xDD,显示出来恰好就是“Ý”标记。在这个时候你向注册表中写数据,字符串的内容虽然全被破 坏了。

我们不该应当即把这个姑且工具转化成 char* 范例,应该先把它生存到一个 CString 工具中,这意味着把姑且工具 复制了一份,所以当姑且的 CString 工具被析构了之后,这个 CString 工具中的值依然生存着。这个时候再向注册表中写数据 就没有问题了。

另外,我的代码是具有 Unicode 意识的。谁人操纵注册表的函数需要一个字节巨细,利用lstrlen(Name+1) 获得的实际功效对付 Unicode 字符来说比 ANSI 字符要小一半,并且它也不能从这个字符串的第二个字符起开始计较,也许你 的本意是 lstrlen(Name) + 1(OK,我认可,我也犯了同样的错误!)。岂论如何,在 Unicode 模式下,所有的字符都是2个字 节巨细,我们需要处理惩罚这个问题。微软的文档令人惊奇地对此保持沉默:REG_SZ 的值毕竟是以字节计较照旧以字符计较呢?我 们假设它指的是以字节为单元计较,你需要对你的代码做一些修改来计较这个字符串所含有的字节巨细。

    关键字:

在线提交作业