呵呵,实在不好意思,楼主,偶犯了个简单错误,这里应该是if (Pchar(Str)+StrLen(PChar(Str)))^=#0 then ....Pchar(Str)返回AnsiString中真正字符数组开始的第一位置 而StrLen返回的长度不包括最后的#0,所以这里不应该加1,呵呵!另外,使用Length来返回长度也是不包括最后一个空字符的....
呵呵,实在不好意思,楼主,偶犯了个简单错误,这里应该是if (Pchar(Str)+StrLen(PChar(Str)))^=#0 then ....Pchar(Str)返回AnsiString中真正字符数组开始的第一位置 而StrLen返回的长度不包括最后的#0,所以这里不应该加1,呵呵!另外,使用Length来返回长度也是不包括最后一个空字符的....
procedure tform1.fenjie (fjstr:string); var t,i,n:integer;s,phone_t,time_t,cnt_t,str:string;ps:pchar; begin fjstr:=tmp_r+fjstr; n:=length(fjstr); //showmessage(inttostr(n)); i:=0; ps:=pchar(fjstr); while i<n do begin if rightstr(ps,1)=#0 then begin showmessage('#0') {tmp_r:=''; t:=strlen(ps); s:=stringofchar('a',t); strcopy(pchar(s),ps); i:=i+t+1; ps:=ps+t+1; form1.memo3.Lines.Add(datetimetostr(now)+' '+s+#13#10+inttostr(t)); //信息格式为:手机号码(11):时间(14):内容#0手机号码(11):时间(14):内容... time_t:=leftstr(s,14);//time_t是发送时间 phone_t:=leftstr(s,26); phone_t:=rightstr(phone_t,11);//phone_t是发送手机号码 cnt_t:=rightstr(s,t-27); //cnt_t是发送内容 str:='update lymud set flag=2,gettext='''+cnt_t+''' where phone='''+phone_t+''' and createtime='''+time_t+''''; with aq3 do begin close; SQL.Clear; SQL.Add(str); ExecSQL; end end else begin//说明最后一条信息没有发送完整 {t:=strlen(ps); tmp_r:=stringofchar('a',t); strcopy(pchar(tmp_r),ps); showmessage('not #0'); end; end; end;
错,大错特错!不是因为使用PChar才把AnsiString后面加了#0,而是在编译开关{$H}打开的情况下,你申明一个String类型的变量,编译器就默认这是一个AnsiString类型,就自动在后面为你添加一个Null结束符,并且和ShortString的存储类型也不一样,而且对于AnsiString类型的字符串,每个字符是AnsiChar组成的,并且保存引用记数功能,所以对于AnsiString的操作一般来说是进行指针的拷贝,所以速度很快!比如 var S1,S2:String; //这个时候由于你没有改变{$H}编译指令的默认状态,所以编译器认为你这里申明的是AnsiString,除非你后面指定了长度 begin S1:='I am an AnsiString!'; //实际上S1是个指针,指向堆中的具体AnsiString存放地址偏移为0的地址,并且到这里引用记数加1 S2:=S1; //到这里引用记数再加1,为2 S2:='I am another AnsiString!'; //到这里,由于S2的长度改变,和S1不一致,所以开始单独分配空间,并且原有引用记数减1,同时S2引用记数加1AnsiString的长度是动态分配的!明白? end;
你这种比喻的确让人误解了 帮助中有这么一段 A long string variable occupies four bytes of memory which contain a pointer to a dynamically allocated string. When a long string variable is empty (contains a zero-length string), the string pointer is nil and no dynamic memory is associated with the string variable. For a nonempty string value, the string pointer points to a dynamically allocated block of memory that contains the string value in addition to a 32-bit length indicator and a 32-bit reference count. The table below shows the layout of a long-string memory block.Offset Contents -8 32-bit reference-count -4 length in bytes 0..Length -1 character string Length NULL character The NULL character at the end of a long string memory block is automatically maintained by the compiler and the built-in string handling routines. This makes it possible to typecast a long string directly to a null-terminated string. For string constants and literals, the compiler generates a memory block with the same layout as a dynamically allocated string, but with a reference count of ?. When a long string variable is assigned a string constant, the string pointer is assigned the address of the memory block generated for the string constant. The built-in string handling routines know not to attempt to modify blocks that have a reference count of -1.ansiString应该是这里的long string吧,它的结构 Offset Contents -8 32-bit reference-count -4 length in bytes 0..Length -1 character string Length NULL character 好像和你的有点出入哟
To BlueCat Help中的结构也不能说不正确,但至少不是很全! 你的那个代码我也实验了,的确是34,和我说的有点出入,我也正在想这种出入是哪里导致的!我现在有一个疑惑,刚才请教了几个人,里面有人说我给出的那个结构里的szBuf应该也是一个指针,而且那个结构不是存放在堆栈里面的!我现在这里有点表示不同意!
FS那个说法是我给出的,但是偶要声明,偶的信息来自BCB的源码,至于DELPHI的实现细节因为没有源码,不太了解。 贴出BCB的源码片断: class RTL_DELPHIRETURN AnsiString { ... protected: ... struct StrRec { int allocSiz; int refCnt; int length; }; ... private: // assert(offsetof(AnsiString, Data) == 0); char *Data; };
strAnsi = record nSize: Integer; nRef: Integer; nLength: Integer; szBuf: array[0..0] of Char; end; 这个结构肯定不是在一个连续的空间上,所以szBuf仅仅是一个指针,它的值应该在另一段空间中,所以导致整个空间多了4个字节,不知道是不是这样?
补充一些: 早年的Pascal对字符串的定义是这样的: str = record nLen : Byte; szBuf: array[0..0] of Char; end;所以,在Pascal里,字符串的第一个字符是[1],而不是像C一样是[0],因为在Pascal里[0]是串长度,所以Pascal的串长度限制为255个字符。DELPHI作了改进,将原来的Pascal String定义为ShortString,而新的String其实就是AnsiString,至于AnsiString的结构是怎么样的,那就不知道了。也许会继承传统,如各位前面所述,也有可能与BCB统一,用BCB的实现方式。
看看帮助中说的:A long-string variable is a pointer occupying four bytes of memory. When the variable is empty--that is, when it contains a zero-length string--the pointer is nil and the string uses no additional storage. When the variable is nonempty, it points a dynamically allocated block of memory that contains the string value. The eight bytes before the location contain a 32-bit length indicator and a 32-bit reference count. This memory is allocated on the heap, but its management is entirely automatic and requires no user code.“....This memory is allocated on the heap....”,这里的memory我想应该是指的“The eight bytes before the location contain a 32-bit length indicator and a 32-bit reference count”里面的The eight bytes和后面的location,而那个location则是指上面的a dynamically allocated block of memory that contains the string value.所以还象我上面给出的那个结构一样,指示长度和引用计数这两块和后面的实际字串内容应该是在一起的!而且他们的内存都是在堆heap上!我这样理解应该没有错误吧!(我怎么感觉我像在做考研英语阅读理解!)
“指示长度和引用计数这两块和后面的实际字串内容应该是在一起的!而且他们的内存都是在堆heap上!”的确是这样但是 A long-string variable is a pointer occupying four bytes of memory. long string要占据4个字节的内存,所以szBuf根本就不存在4个字节的指针空间那么 s:='My name is delphi';//长17 strAnsi = record nSize: Integer;//4 nRef: Integer; //4 nLength: Integer;//4 szBuf: array[0..16] of Char; //17,不存在4字节的指针 end; 另外4个字节就是s指针(指向整个结构),也就是ansistring变量,还有一个null,所以s共占去34个字节这样对否?
strlen:integer;
begin
strlen:=length(str);
if str[strllen]=#0 then
showmessage('it is null');
end;
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: array of Char;
end;并且szBuf的最后一位为#0,所以这里如果想判断,可以如下进行(指向AnsiString的指针指向AnsiString的偏移为0,既szBuf的第一个字符处):if (Pchar(Str)+StrLen(PChar(Str)))^=#0 then ....
把分给fs 吧 他说得不错
她只是与空结束的字符窗相兼容 无长度限制
除非你自己强制添加空结束 要不然最后一位不会是空
pchar 是自己强制添加的 所以为避免内存错误dll中都用pchar 这个我想大家都知道
其实一楼的方法可以判断强制添加的空字符
s:string; s:='1234'#0#0'34';
ShowMessage(IntToStr(Length(s)));// 显示 8
ShowMessage(IntToStr(StrLen(PChar(s)))); //显示 4所以,string 中可以存储 #0, 而 PChar 是以 #0 终结
而StrLen返回的长度不包括最后的#0,所以这里不应该加1,呵呵!另外,使用Length来返回长度也是不包括最后一个空字符的....
而StrLen返回的长度不包括最后的#0,所以这里不应该加1,呵呵!另外,使用Length来返回长度也是不包括最后一个空字符的....
除非你自己
var s:string;
s:='12345678';
S[Length(s)]=#0;
ansistring和pchar的区别大家都知道,为什么object pascal要弄一个ansistring,而不是直接使用c/c++里的pchar(当然c/c++里不叫pchar,而叫char*)?就是为方便使用,为什么非要给ansistring后面加一个0呢?
世界变了,人也变了。
ansistring和pchar的区别大家都知道,为什么object pascal要弄一个ansistring,而不是直接使用c/c++里的pchar(当然c/c++里不叫pchar,而叫char*)?就是为方便使用,为什么非要给ansistring后面加一个0呢?
世界变了,人也变了。
string,ansistring: 则最后一个字符是 s[Length(s)]。
PChar: 则必须以 #0 终结,否则,将访问到其它空间。
var t,i,n:integer;s,phone_t,time_t,cnt_t,str:string;ps:pchar;
begin
fjstr:=tmp_r+fjstr;
n:=length(fjstr);
//showmessage(inttostr(n));
i:=0;
ps:=pchar(fjstr);
while i<n do
begin
if rightstr(ps,1)=#0 then
begin
showmessage('#0')
{tmp_r:='';
t:=strlen(ps);
s:=stringofchar('a',t);
strcopy(pchar(s),ps);
i:=i+t+1;
ps:=ps+t+1;
form1.memo3.Lines.Add(datetimetostr(now)+' '+s+#13#10+inttostr(t)); //信息格式为:手机号码(11):时间(14):内容#0手机号码(11):时间(14):内容...
time_t:=leftstr(s,14);//time_t是发送时间
phone_t:=leftstr(s,26);
phone_t:=rightstr(phone_t,11);//phone_t是发送手机号码
cnt_t:=rightstr(s,t-27); //cnt_t是发送内容
str:='update lymud set flag=2,gettext='''+cnt_t+''' where phone='''+phone_t+''' and createtime='''+time_t+'''';
with aq3 do
begin
close;
SQL.Clear;
SQL.Add(str);
ExecSQL;
end
end
else
begin//说明最后一条信息没有发送完整
{t:=strlen(ps);
tmp_r:=stringofchar('a',t);
strcopy(pchar(tmp_r),ps);
showmessage('not #0');
end;
end;
end;
tmp_r是在这个位置定义的全局变量:var
Form1: TForm1;
tmp_r:string;
implementation
var
S1,S2:String; //这个时候由于你没有改变{$H}编译指令的默认状态,所以编译器认为你这里申明的是AnsiString,除非你后面指定了长度
begin
S1:='I am an AnsiString!'; //实际上S1是个指针,指向堆中的具体AnsiString存放地址偏移为0的地址,并且到这里引用记数加1
S2:=S1; //到这里引用记数再加1,为2
S2:='I am another AnsiString!'; //到这里,由于S2的长度改变,和S1不一致,所以开始单独分配空间,并且原有引用记数减1,同时S2引用记数加1AnsiString的长度是动态分配的!明白?
end;
但 PChar 占用的空间包括这个 #0 所占的空间
但 PChar 占用的空间包括这个 #0 所占的空间
能不能举个简单的例子说明一下这些类型的关系啊,比如string,ansistring,widestring,pchar,pwidechar,不问还可以,一问现在脑子一团糟,谢谢了!
s:string;
pc:PChar;
i:Integer; s:='1234';
GetMem(PC,Length(s)+1);
for i:=1 to Length(s) do
PC[i-1]:=s[i]; PC[i]:=#0;
s:string;
pc:PChar;
i:Integer; s:='1234';
GetMem(PC,Length(s)+1);
for i:=1 to Length(s) do
PC[i-1]:=s[i]; PC[i]:=#0;
s:string;
pc:PChar;
i:Integer; s:='1234';
GetMem(PC,Length(s)+1);
for i:=1 to Length(s) do
PC[i-1]:=s[i]; PC[i]:=#0;
我是指如果要把一个 string 传给一个 PChar 类型,给 PChar 分配空间时,要比字符串长度多 1 个空间。
var
s:string;
pc:PChar;
i:Integer; s:='1234';
GetMem(PC,Length(s)+1);
for i:=1 to Length(s) do
PC[i-1]:=s[i]; PC[i]:=#0;
var
S:String[32]; //这里因为指定了长度,所以虽然处于{$H+}状态仍然为ShortString这里的长度最长可以指定为255,如果指定长度超过255,比如256,马上就会报一个编译错误,警告越界!这说明虽然处于{$H+}状态,但指定长度仍然不能超过ShortString的范围限制!AnsiString是由AnsiChar字符组成的,而对于WideString则是有WideChar组成的,也就是宽字符,实际上就是两个字节!这样做的目的是为了满足使用Ansi字符集无法表示的字符,例如中文字符,WideChar使用Unicode字符集,几乎可以表示任何字符,对于WIN NT以下的操作系统不支持Unicode字符集!所以WideString和AnsiString没有多大的区别,唯一区别就是组成的字符大小不一致!对于PChar、PAnsiChar和PWideChar都是指针,所以长度永远都是四个字节,分别指向Char、AnsiChar和WideChar组成的以Null结尾的字符串!最后就是Char、AnsiChar和WideChar的区别:
Char目前相当于AnsiChar,将来版本的Delphi中相当于WideChar。这里的意思可能是将来Ansi字符集或许会废弃不用,全部使用Unicode 字符集!
WideChar是2字节的Unicode字符;而AnsiChar就是1个字节的Ansi字符!好了,就这么多了,更加详细的内容网络上可以查到很多.....
你说AnsiString类型的结构如下:
strAnsi = record
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: array of Char;
end;
那请问我为什么不能跟操作结构类型一样的操作AnsiString呢?
还用你怎么知道它的结构是这个样子的?有点不合理吧,怎么会是这样的呢?
| 分配大小 | 引用计数 | 长度 | 具体字符串内容 .... | #0 |
----------------------------------------------------------------
对于以Null结尾的字符,都是动态分配空间的,而且没有255的长度限制,而我们上面所说的PChar、PAnsiChar和PWideChar都是指针,他们的内容所指向的偏移为0的地址应该都是上图从左到右第四段那里的起始地址........
帮助中有这么一段
A long string variable occupies four bytes of memory which contain a pointer to a dynamically allocated string. When a long string variable is empty (contains a zero-length string), the string pointer is nil and no dynamic memory is associated with the string variable. For a nonempty string value, the string pointer points to a dynamically allocated block of memory that contains the string value in addition to a 32-bit length indicator and a 32-bit reference count. The table below shows the layout of a long-string memory block.Offset Contents
-8 32-bit reference-count
-4 length in bytes
0..Length -1 character string
Length NULL character
The NULL character at the end of a long string memory block is automatically maintained by the compiler and the built-in string handling routines. This makes it possible to typecast a long string directly to a null-terminated string.
For string constants and literals, the compiler generates a memory block with the same layout as a dynamically allocated string, but with a reference count of ?. When a long string variable is assigned a string constant, the string pointer is assigned the address of the memory block generated for the string constant. The built-in string handling routines know not to attempt to modify blocks that have a reference count of -1.ansiString应该是这里的long string吧,它的结构
Offset Contents
-8 32-bit reference-count
-4 length in bytes
0..Length -1 character string
Length NULL character
好像和你的有点出入哟
要把一个 string(中间没有#0) 传给一个 PChar 类型,
给 PChar 分配空间时,可肯定要比字符串长度多 1 个空间。
var
s:string;
pc:PChar;
i:Integer; s:='1234';
GetMem(PC,Length(s)+1);
move(s[1],pc[0],Length(s)+1);
给 PChar 分配空间时,可肯定要比字符串长度多 1 个空间。”为什么这么说?var
s:string; //这里如果编译指令为{$H+},那么这里这个S就是一个AnsiString类型变量
pc:PChar;
i:Integer; s:='1234'; //这里不知道楼上认为分配多少个空间,个人认为分配空间大小为4+4+4+1*4+1=17个字节的空间
GetMem(PC,Length(s)+1); //为什么要给PChar这样分配空间?
move(s[1],pc[0],Length(s)+1);
分配Refcount(4)+Lenth(4)+实际字符串(4)+#0(1)
对于最后的#0,AnsiString绝对是要分配的,否则Pchar(s)根本不可能实现!!!
Borland不可能为Pchar(s)重新分配内存,也没有此必要,因为已开始已分配了Length(s)+1的空间,并且确保s[length(s)]=#0,所以中间含有#0的AnsiString其实非常危险,特别是Pchar(s)的调用肯定会出问题!!!
一句话,若没有特殊理由的话,AnsiString S 中间就不应该有#0,但是它的
最后S[Length(S)+1]不是S[Length(S)],则肯定为0!!!
打字真累!!!
pc:=PChar(s);
这样的话,当然不需要为 PC 分配空间。
而我的意思是把 s 的内容复制到由 PC 所指向的一块内存空间,这时,为 PC 分配空间时就要多一个 #0 的空间了。
var p:pchar;
s:string;
s:='12345678';
分配Refcount(4)+存放实际长度(4)+实际字符串(8)+#0(1)
是不是这样分配空间可以去问Borland
p:=Pchar(s);
简单的PChar(s)强制转换,确实是没有再重新分配内存,因为S已经分配了内存
但是如果要复制它,则要分配Length(s)+1个空间
getmem(p,Length(s)+1);
strcopy(p,pchar(s));//p结尾自动加#0;难道这还有疑问!!!
getmem应该是给指针指定一个可操作的地址范围(内存空间)
strcopy就应该是将具体的字符串copy到这个地址范围(内存空间)里,这个地址就是指针指向的地址所以,大家都是正确的
s:string;
itemp:integer;
begin
s:='My name is delphi';//17个字节
itemp:=integer(pointer((integer(@s[1])-4))^); //字符串长度等于17 正确
showmessage(inttostr(itemp));
itemp:=integer(pointer((integer(@s[1])-8))^); //引用计数等于1 正确
showmessage(inttostr(itemp));
itemp:=integer(pointer((integer(@s[1])-12))^); //占用空间等于34 不正确 应该是:4+4+4+17+1=30;
showmessage(inttostr(itemp));
end;FrameSniper你如何解释?
var
s:string;
itemp:integer;
begin
s:='My name is delphi';//17个字节
itemp:=integer(pointer((integer(@s[1])-4))^); //字符串长度等于17 正确
showmessage(inttostr(itemp));
itemp:=integer(pointer((integer(@s[1])-8))^); //引用计数等于1 正确
showmessage(inttostr(itemp));
itemp:=integer(pointer((integer(@s[1])-12))^); //占用空间等于34 不正确 应该是:4+4+4+17+1=30;
showmessage(inttostr(itemp));
end;你如何解释?
strAnsi = record
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: array of Char;
end;这是我前面给出的结构示意,和你说的4+4+4+17+1的结果有什么区别吗?
按你给的结构,占用4+4+4+17+1=30个字节,没错吧?我这样计算s占用空间:
integer(pointer((integer(@s[1])-12))^); //应该没错吧?
为什么占用了34个字节?
strAnsi = record
nSize: Integer;//4
nRef: Integer; //4
nLength: Integer;//4
szBuf: array[0..0] of Char; //4
end;
所以应该是4+4+4+4+17+1
strAnsi = record
nSize: Integer;//4
nRef: Integer; //4
nLength: Integer;//4
szBuf: array[0..0] of Char; //4
end;
所以应该是4+4+4+4+17+1需要明白的一点是,AnsiString本身就是一个指针,这个指针指向的是堆栈中的字符串第一个字符的位置上!如果你说这里的szBuf前面还有一个指针,我不明白这个指针里面保存的内容是什么,因为这个结构表示的内存空间应该是在堆栈上的!
我是没搞董,为什么szBuf已经占用了17个字节,它还占有一个指针(4个字节)的空间?
我想,既然szBuf是一个指针,那就应该占有一个4字节的空间
那么,s(AnsiString)也是一个指针,为什么就不该再占用4字节的空间呢?
难道s这个指针恰好和szBuf这个指针重合(就是一个地址)?这样就解释清楚了?
所以FrameSinper给出的ansistring的结构才是正确的?help中的结构是不全的?
贴出BCB的源码片断: class RTL_DELPHIRETURN AnsiString
{
...
protected:
...
struct StrRec {
int allocSiz;
int refCnt;
int length;
};
...
private:
// assert(offsetof(AnsiString, Data) == 0);
char *Data;
};
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: array[0..0] of Char;
end;
这个结构肯定不是在一个连续的空间上,所以szBuf仅仅是一个指针,它的值应该在另一段空间中,所以导致整个空间多了4个字节,不知道是不是这样?
早年的Pascal对字符串的定义是这样的:
str = record
nLen : Byte;
szBuf: array[0..0] of Char;
end;所以,在Pascal里,字符串的第一个字符是[1],而不是像C一样是[0],因为在Pascal里[0]是串长度,所以Pascal的串长度限制为255个字符。DELPHI作了改进,将原来的Pascal String定义为ShortString,而新的String其实就是AnsiString,至于AnsiString的结构是怎么样的,那就不知道了。也许会继承传统,如各位前面所述,也有可能与BCB统一,用BCB的实现方式。
我想这两者的区别就大了
A long-string variable is a pointer occupying four bytes of memory.
long string要占据4个字节的内存,所以szBuf根本就不存在4个字节的指针空间那么
s:='My name is delphi';//长17
strAnsi = record
nSize: Integer;//4
nRef: Integer; //4
nLength: Integer;//4
szBuf: array[0..16] of Char; //17,不存在4字节的指针
end;
另外4个字节就是s指针(指向整个结构),也就是ansistring变量,还有一个null,所以s共占去34个字节这样对否?
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: array[0..0] of Char;
end;
szbuf其实就是Ansistring的指针
strAnsi = record
nSize: Integer;
nRef: Integer;
nLength: Integer;
szBuf: pChar;
end;
szbuf
现在小弟正在学汇编,请多指教,在此谢过
然后,根据nLen创建一个Instance
在NewInstance时,计算所需要空间:
nSize := ( nLen + 10 ) and ( -2 ); // nLen=$11时,nSize=$1A
估计上述算法是基于:加上8Byte的nRefCnt和nLen,及最后一个#0,然后按16bit Word对齐
然后,调用GetMem分配内存,设分配的地址为pAddr
其结构如下:
strAnsi = record
unknown : Integer;
nSize : Integer;
nRefCnt : Integer; // pAddr指向这里
nLen : Integer;
szBuf : Array [0..17] of char; // 包括最后的#0所占空间
end;
然后,将Result设置为pAddr+$8(即指向szBuf)
再把nLen赋给strAnsi.nLen,把1(refCnt)赋给strAnsi.nRefCnt好啦,分析完毕,显然:
一个String的Instance中只包括:nRefCnt,nLen,szBuf三部分,前面的nSize是分配内存时留下的,至于nSize前面一个是什么东东,偶也不知道了,应该是DELPHI内存管理用的东东,这也就是为什么nSize总是会多出来的四个byte了。
@LStrFromPCharLen:根据一个长度来创建一个String的Instance
@NewAnsiString:根据Len创建一个新的String Instance
@GetMem:分配内存有兴趣的可以自己在CPU Windows里看:)
我喜欢以上大虾们的这种辩论
能让人深入的了解Delphi底层的东西,有益我等菜鸟的健康成长
佩服
感谢大虾们