BCB2010 关于char* 的小问题void __fastcall TForm1::Button1Click(TObject *Sender){Label1-Caption E
BCB2010 关于char* 的小问题
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = Edit1->Text;
AnsiString str;
char *str1;
String str2;
str = Edit1->Text;
str1 = Edit1->Text.t_str();
Label2->Caption = str1;
str2 = str1;
Label3->Caption = str2;
}
为什么 在执行完这一句后
Label2->Caption = str1;
str1的值变成1了?
[解决办法]对,是这样的
char *str1 = Edit1->Text.t_str();//返回的只是临时指针,当这行结束,指针就结束了
Label2->Caption = str1; //这句就是错误的,恰好内存中的临时变量还没被破坏
//下一行就出错
应该改成
wchar_t *str1 = Edit1->Text.t_str();//就好多了
[解决办法]慎用 .c_str()/.t_str()/.w_str() 的返回值。特别是不要 char *p = str.c_str(); 这样使用。
[解决办法]简单例程:
String strText = "abc";
// 记录String对象的.c_str()返回指针
wchar_t *p = strText.c_str();
// 显示出p所指向的信息
ShowMessage(String().sprintf(TEXT("p 长度: %d, 内容: %s"), wcslen(p), p));
// 重新给String对象赋个值
strText = "defghijklmn";
// 再次Show出p的信息,按理说,应该是显示新的"defghijklmn"
// 但是结果显示仍然是"abc",也就是说p仍然指向上一次显示的内容
// 而这块内存区域有可能被别的数据覆盖
ShowMessage(String().sprintf(TEXT("p 长度: %d, 内容: %s"), wcslen(p), p));
// 正确的值应该是:
p = strText.c_str();
ShowMessage(String().sprintf(TEXT("str 长度: %d, 内容: %s"), strText.Length(), p));
// 建议直接使用String对象的.c_str(),尽量不要把这个指针赋给p然后在后面的代码中再使用p
ShowMessage(String().sprintf(TEXT("str 长度: %d, 内容: %s"), strText.Length(), strText.c_str()));
以上代码适用于C++Builder2009/2010/XE,因为2009以后的版本中String映射为UnicodeString,所以String对象的.c_str()返回的是wchar_t *类型的值,如果要返回char *数据,可以用AnsiString强制转换再通过.c_str()获取。
[解决办法]1、分清对象的生命期,是否临时对象.对于有属性的BCB来说,更是要注意这一点.
String str = Edit1->Text ;
wchar_t *p1 = str.c_str();
wchar_t *p2 = Edit1->Text.c_str();
这里会有多个对象产生,
1) str 这个被声明的对象 及 p1 p2 两个指针,在函数中均是局部变量
2) Edit1->Text 调用该属性,会返回一个临时对象.
3) C++ 被人批的其中一个原因就是很容易不恰当地产生多余的临时对象,造成效率低下.
2.注意属性带来的不确定性.为了程序安全可靠,我们调用属性,就认为是调用一个函数的返回值(右值).
值得我们注意的是并非调用所有的String属性都会产生一个临时对象,这要看该属性是如何实现的,调用 Edit1->Text 是产生一个临时对象的.一般来说,属性声明为
read = GetXXXX 这种方式的均产生一个临时对象, read = FText 类型的,则没有产生额外的对象,
调用Edit1->Text.c_str();相当于 Edit1->GetText().c_str();
3. 一个指向临时对象内部的指针是无意义的
wchar_t *p2 = Edit1->Text.c_str();//这个就是无意义的野指针.
4.在对象的生命期内,使用指向其内部的指针是可以的
wchar_t *p1 = str.c_str();//这个是可以的,在str 的生命还没结束前是可用的.
5.用指针得时刻注意内存的变化.
String str = Edit1->Text ;
wchar_t *p1 = str.c_str();
str = "New Text" ; //此时,内存已变化,指针p1已无意义.
6.慎用指针,特别注意不要破坏对象的内存布局.
str = "New Text" ;
wchar_t *p1 = str.c_str();
*(p1+2) = 0 ; //这个写操作破坏了对象的内存布局,使对象对内存的解析产生不确定性
ShowMessage(str);
ShowMessage(str+"为什么这个串不会被显示.");
c_str()本应返回的是一个 const 指针,但因为种种原因,BCB并没有如此实现.
7.注意 String 对象的引用机制.String 类采用写时复制的方法来提高复制时的效率,写代码时要时刻注意这一点.
String str1 = "New Text" ;
String str2 = str1; //此时,str2 str1 共用一个字符串
String str3 = str1.c_str(); //此时,str3没有与 str2 str1 共用一个字符串,是另外分配内存,并做字符串复制了.
wchar_t *p1 = str2.c_str();
*p1 = 'A' ; //这个操作,同时修改了 str1 str2 , 因为str1 str2 是共用这块内存的
写时复制,为我们带来了效率,但同是也带来了内存管理的复杂性,当操作指针时,要慎重.
最后,因为以上种种原因, 建议慎用 c_str() ; t_str();data() ; 等函数,而t_str()则更是离谱,调用它,可能字符串会因此改变,我们在编程中,最怕的就是可能会变化,但不一定会变. 所以更要慎用.
[解决办法]
这是因为我在的测试工程中,_TCHAR映射为wchar_t,String格式化的时候,常量字符串就是wchar_t *的。而你的应用中,工程选项中的_TCHAR映射为char, 所以报类型不匹配。这个和我要解释的东西其实没多大关联。
9楼大牛PPower解释的灰常清楚了。
[解决办法]对于复制的时候尽量使用strcpy()、_tcscpy(), 不要直接返回给指针
如果一个函数要求char*,wchar_t *参数,可以使用c_str() ; t_str()方法;
比如:
#include <TCHAR.h>
#include <stdio.h>
TCHAR c[20];
String s="你好";
_tcscpy(c,s.t_str()); //_tcscpy()
char c1[20];
AnsiString s1="1234";
strcpy(c1,s1.c_str()); //strcpy()
char c2[100];
AnsiString s3 = "Hello World!";
sprintf(c2,"%s", s3.c_str());//printf()
这样不会出错,因为函数调用c_str(),t_str()返回的是一个临时指针的参数,不能对其进行操作
所以建议除了函数需要求使用char*,wchar_t *参数时,其余的地方坚决不用
[解决办法]String str1 = "New Text" ;
String str2 = str1; // str2 沒有直接復制字符串, 而且共用了.
if(str1.c_str() == str2.c_str())//這時兩個指針指向同樣的內存
ShowMessage("共用");
else
ShowMessage("不共用");
str1 = "other" ;//寫時復制一份。不影響str2
if(str1.c_str() == str2.c_str())//此時內存已經不同 了