初学指针的小问题? ( 积分: 50 )

  • 主题发起人 主题发起人 dpbug_cn
  • 开始时间 开始时间
D

dpbug_cn

Unregistered / Unconfirmed
GUEST, unregistred user!
请指教,为什么下边那一行代码运行时出错?

type
TTest = record
a: string;
b: Integer;
c: string;
..........

procedure xxxxx;
var
P := ^TTest;
begin
GetMem(P, SizeOf(TTest));
P^.a := '1';
P^.b := 10;
P^.c := '2'
// 这一行报错 !!! 什么原因?
Showmessage(P^.a);
FreeMem(P);
end;
只要TTest里边定义了两个或两个以上string,给第二个string赋值时就会报错,只要定义一个string,就不会报错,运行正常,我换用了array of string也不行,只要给第二个string赋值就出错。
原因何在? 请指教!
 
请指教,为什么下边那一行代码运行时出错?

type
TTest = record
a: string;
b: Integer;
c: string;
..........

procedure xxxxx;
var
P := ^TTest;
begin
GetMem(P, SizeOf(TTest));
P^.a := '1';
P^.b := 10;
P^.c := '2'
// 这一行报错 !!! 什么原因?
Showmessage(P^.a);
FreeMem(P);
end;
只要TTest里边定义了两个或两个以上string,给第二个string赋值时就会报错,只要定义一个string,就不会报错,运行正常,我换用了array of string也不行,只要给第二个string赋值就出错。
原因何在? 请指教!
 
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TTest = record
a: pchar;
b: Integer;
c: pchar;
end;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
P : ^TTest;
begin
GetMem(P,SizeOf(TTest))

P^.a := '1';
P^.b := 10;
P^.c := '2'
// 这一行报错 !!! 什么原因?
Showmessage(P^.a);
Showmessage(inttostr(P^.b));
Showmessage(P^.c);
FreeMem(P);
end;

end.
 
c: string;
改成
c: ShortString;
即可
 
什么原因呢?为什么string不行?请解释一下
 
string和PChar也是指针,而且比较特殊,Delphi分配内存的时候,在record里面会出现你说的问题。指针就4个字节,分配后指向的地址不一定是正确的。而ShortString和string[xxxx]则等于array of char,是数组,所以在分配record的时候能够自动分配足够内存。
 
能不能解释一下为什么不能用string ? 谢谢
 
字符串string 字符数组与指向字
  符串的指针pchar的区别与联系
  这3者的基本概念相同,但有一些非常细微的差别,在编程时稍不注意就会出错,需高度重视。
  1、使用指向字符串的指针,如果不是以0结尾,运行时就会出现错误。为了避免这种错误,需要在字符串结尾人工加入0 即char(0),或用strpcopy函数在字符串结尾自动加0。
  例1: 指向字符串的指针,如果不是以0结尾,运行时会出现错误(pchar是以0#结尾,而string不是):
  {s[0]=3 s[1]='n' s[2]='e' s[3]='w'}
  var
  s:string;
p:pchar;
  begin
  s:='new'

  label1.caption:=s
{new}
 label2.caption:=intTostr(integer(s[0]));{3是字符串的长度}
  p:=@s[1];{不是以0结尾,莫用pchar型指针}
   label3.caption:=strpas(p)
{运行时出现错误}
  end;

  例2:在字符串结尾人工加入0即char(0),可使用指向字符串的指针。
  {s[0]=4 s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
  {p-->'new'}
  var
s:string

p:pchar;
  begin
  p:=@s[1];
  s:='new'+char(0)
{以0结尾,可用pchar型指针}
  label1.caption:=strpas(p)
{new}
  label2.caption:=s
{new}
   label3.caption:=intTostr(integer(s[0]))
{4是字符串长度}
  end;

  例3: 用strpcopy函数赋值会在字符串s结尾自动加0。
  {s[0]=4 s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
  {p-->'new'}
  var
s:string

p:pchar;
  begin
  p:=@s[1];
 strpcopy(p,'new');{strpcopy函数在字符串结尾自动加0}
  label1.caption:=strpas(p);{new}
   label2.caption:=s;{new}
  label3.caption:=intTostr(integer(s[0]));{4}
  end;

  2、下标为0的字符串标识符存放的是字符串长度,字符型数组基本相当于字符串,但不能存放字符串长度。字符串可以用s:='a string'的形式赋值,但是字符型数组a[ ]不可直接用a:='array'的形式赋值,用此种形式会出现类型不匹配错误,可选用strpcopy函数赋值。

  例4: 字符型数组s[ ]相当于字符串,但没有存放字符串长度的位置。
  {s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
  {p-->'new'}
  var
s:array[1..10] of char

p:pchar;
  begin
  {s:='new'+char(0)
error}{错误}
  p:=@s[1];
  {p:=@s
is not correct}
  strpcopy(p,'new');
  label1.caption:=strpas(p);{new}
  label2.caption:=s;{new}
   {label3.caption:=intTostr(integer(s[0]));}{不会出现4, 下标超出错误}
  end;

  例5:下标从0开始的字符数组s,s相当于@s[0]。
  { s[0]='n' s[1]='e' s[2]='w' s[3]=0;}{p-->'new'}
  var
s:array[1..10] of char

p:pchar;
  begin
  {s:='new'+char(0)
error}{错误}
  p:=s;
  {p:=@s[0] is also correct}
  strpcopy(p,'new');
  label1.caption:=strpas(p);{new}
  label2.caption:=s;{new}
  label3.caption:=s[0];{n}
end;

  3、下标从0开始和从1开始的字符数组地址的表示方法也有细微不同:

  例6:下标从1开始的数组a 与下标从0开始的数组b 的比较。
  var
a:array[1..10]of char

b:array[0..10]of char;
  {a:='1..10';}{type mismatch}
  {b:='0..10';}{type mismatch}
begin
  strpcopy( b, 'from 0 to 10')
{正确 因为b即是@b[0] }
  strpcopy(@b[0], 'from 0 to 10')
{正确 与上个表达式结果相同}
  strpcopy(@a[1], 'from 1 to 10')
{正确 }
  strpcopy( a, 'from 1 to 10')
{类型匹配错误 因为a即是@a[0]}
end;
 
基本上,这个问题与string无关,是分配内存时出的问题。因为对于一个指向记录数据类型的指针来说,为其分配内存应该使用new过程而不是GetMem过程。所以你的程序只要改成这样就不会有问题了:

type
TTest = record
a: string;
b: Integer;
c: string
end;
var
P : ^TTest;
begin
new(P);
P^.a := '1';
P^.b := 10;
P^.c := '2'

Showmessage(P^.a);
dispose(P);
end;
 
你这样定义不好用

pn=^nn
nn=record
a:string;
b:string;
end;

pa:pn;
new(pa);
pa.a:='aaa';
pa.b:='bbb';
dispose(pa);
这样多好
 
GetMem也可以,但是在使用之前请ZeroMemory/FillChar(.., 0);等初始化.
 
type
PTest = ^TTest
最好这样写
TTest = record
a: string;
b: Integer;
c: string;
..........

procedure xxxxx;
var
P := ^TTest
//能这样吗?知道p: PTest行.
begin
GetMem(P, SizeOf(TTest))
//string不能直接用getmem来分配内存,原因找下生存期自管理的资料看。改成new(p)就好了.
P^.a := '1';
P^.b := 10;
P^.c := '2'
// 这一行报错 !!! 什么原因?
Showmessage(P^.a);
FreeMem(P)
//这里要改成dispose(p);
 
string类型是高级类型,需要初始化
在var下声明string
用new过程
或是作为类成员时,类的Create构造过程等等
都会自动初始化string
但GetMem只能分配内存不能生成初始化代码,因为它不理会类型

换句话说,P是个指针,而指向的TTEST里面A、C也可以认为是指针
P在使用前要初始化,而两个指针成员使用前也不能什么都不做
虽然说string能自己管理自己,但因为一开始就指向非法地址,所以它就脑子混乱了
 
赞同SparkV,和someset,的观点
 

Similar threads

S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
900
SUNSTONE的Delphi笔记
S
D
回复
0
查看
867
DelphiTeacher的专栏
D
D
回复
0
查看
836
DelphiTeacher的专栏
D
后退
顶部