首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ Builder >

BCB2010 关于char* 的小疑点

2013-01-28 
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()则更是离谱,调用它,可能字符串会因此改变,我们在编程中,最怕的就是可能会变化,但不一定会变. 所以更要慎用.

[解决办法]

引用:
报了8条错误:....


这是因为我在的测试工程中,_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())//此時內存已經不同 了

热点排行