Object Pascal 支持像 C 中那样的数组指针吗?(100分)

  • 主题发起人 主题发起人 alexbro
  • 开始时间 开始时间
A

alexbro

Unregistered / Unconfirmed
GUEST, unregistred user!
如下一段程序:

var
P: Pchar;
I: Integer;
begin
......

GetMem(P, 255);
for I := 0 to 254 do
begin
......
P := '';
end;

FreeMem(P);
.......
end;

我见一个例程上这样用,而且我这样用过也是可以的。P 声明为一个指针,但是又能做为一个数组来用,好像 C 里边可以这样用,但是我怎么没有在有关 Object Pascal 的手册上见过这样的说明呢,有关数组和指针的部分都没有。
 
啥,再说一遍?
 
为啥没有人回答哩
 
这肯定不行的。 p是个pchar, 只有string,widestring是数组形式的。
 
你看看TStringList的源代码,应该会有点了解。
其内在也是指针
 
只要数据在内存中是连续存放的,就可以用指针移位访问了,类似你说的数组指针吧
 
pchar是可以用[]访问的
 
你只要抓住,P:PCHAR等于CHAR * P;而P是一个指针变量,P指向一个数组时,就是指向数组的首地址.所以申请内存空间也是很正常的..
var
P: Pchar;
I: Integer;
begin
......

GetMem(P, 255);
for I := 0 to 254 do
begin
......
P := '';
end;

FreeMem(P);
.......
end;
 
底下自已看一下就知道了..
var
test:array[0..100] of integer
@test[0]=@test

浅谈Object Pascal的指针
作者:Nicrosoft 阅读:5154 次 时间:8/26/2001 来源:Nicrosoft个人网站

  大家都认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是C语言的灵魂,一点都不为过。同时,这种说法也让很多人产生误解,似乎只有C语言的指针才能算指针。Basic不支持指针,在此不论。其实,Pascal语言本身也是支持指针的。从最初的Pascal发展至今的 Object Pascal,可以说在指针运用上,丝毫不会逊色于C语言的指针。

  以下内容分为八个部分,分别是

  一、类型指针的定义
  二、无类型指针的定义
  三、指针的解除引用
  四、取地址(指针赋值)
  五、指针运算
  六、动态内存分配
  七、字符数组的运算
  八、函数指针

  一、类型指针的定义。对于指向特定类型的指针,在C中是这样定义的:
    int *ptr;
    char *ptr;
    与之等价的Object Pascal是如何定义的呢?
    var ptr : ^Integer;
      ptr : ^char;
    其实也就是符号的差别而已。

  二、无类型指针的定义。C中有void *类型,也就是可以指向任何类型数据的指针。Object Pascal为其定义了一个专门的类型:Pointer。于是,
    ptr : Pointer;
    就与C中的
    void *ptr;
    等价了。

  三、指针的解除引用。要解除指针引用(即取出指针所指区域的值),C 的语法是 (*ptr),Object Pascal则是 ptr^。

  四、取地址(指针赋值)。取某对象的地址并将其赋值给指针变量,C 的语法是
    ptr = &Object;
    Object Pascal 则是
    ptr := @Object;
    也只是符号的差别而已。

  五、指针运算。在 C 中,可以对指针进行移动的运算,如:
    char a[20];
    char *ptr=a;
    ptr++;
    ptr+=2;
    当执行ptr++;时,编译器会产生让ptr前进sizeof(char)步长的代码,之后,ptr将指向a[1]。ptr+=2;这句使得ptr前进两个sizeof(char)大小的步长。同样,我们来看一下Object Pascal中如何实现:
    var a : array [1..20] of Char;
      ptr : PChar
//PChar 可以看作 ^Char
    begin
      ptr := @a;
      Inc(ptr)
// 这句等价于 C 的 ptr++

      Inc(ptr, 2)
//这句等价于 C 的 ptr+=2;
    end;

  六、动态内存分配。C语言中,使用malloc()库函数分配内存,free()函数释放内存。如这样的代码:
    int *ptr, *ptr2;
    int i;
    ptr = (int*) malloc(sizeof(int) * 20);
    ptr2 = ptr;
    for (i=0
i<20
i++){
      *ptr = i
ptr++

    }
    free(ptr2);
    Object Pascal中,动态分配内存的函数是GetMem(),与之对应的释放函数为FreeMem()(传统 Pascal中获取内存的函数是New()和 Dispose(),但New()只能获得对象的单个实体的内存大小,无法取得连续的存放多个对象的内存块)。因此,与上面那段C的代码等价的Object Pascal的代码为:
    var ptr, ptr2 : ^integer;
      i : integer;
    begin
      GetMem(ptr, sizeof(integer) * 20)

        //这句等价于C的 ptr = (int*) malloc(sizeof(int) * 20)

      ptr2 := ptr
//保留原始指针位置
      for i := 0 to 19 do
      begin
        ptr^ := i;
        Inc(ptr);
      end;
      FreeMem(ptr2);
    end;
    对于以上这个例子(无论是C版本的,还是Object Pascal版本的),都要注意一个问题,就是分配内存的单位是字节(BYTE),因此在使用GetMem时,其第二个参数如果想当然的写成 20,那么就会出问题了(内存访问越界)。因为GetMem(ptr, 20);实际只分配了20个字节的内存空间,而一个整形的大小是四个字节,那么访问第五个之后的所有元素都是非法的了(对于malloc()的参数同样)。

  七、字符数组的运算。C语言中,是没有字符串类型的,因此,字符串都是用字符数组来实现,于是也有一套str打头的库函数以进行字符数组的运算,如以下代码:
    char str[15];
    char *pstr;
    strcpy(str, &quot;teststr&quot;);
    strcat(str, &quot;_testok&quot;);
    pstr = (char*) malloc(sizeof(char) * 15);
    strcpy(pstr, str);
    printf(pstr);
    free(pstr);
    而在Object Pascal中,有了String类型,因此可以很方便的对字符串进行各种运算。但是,有时我们的Pascal代码需要与C的代码交互(比如:用Object Pascal的代码调用C写的DLL或者用Object Pascal 写的DLL准备允许用C写客户端的代码)的话,就不能使用String类型了,而必须使用两种语言通用的字符数组。其实,Object Pascal提供了完全类似C的一整套字符数组的运算函数,以上那段代码的Object Pascal 版本是这样的:
    var str : array [1..15] of char;
      pstr : PChar
//Pchar 也就是 ^Char
    begin
      StrCopy(@str, 'teststr');
        //在C中,数组的名称可以直接作为数组首地址指针来用
        //但Pascal不是这样的,因此 str前要加上取地址的运算符
      StrCat(@str, '_testok');
      GetMem(pstr, sizeof(char) * 15);
      StrCopy(pstr, @str);
      Write(pstr);
      FreeMem(pstr);
    end;

  八、函数指针。在动态调用DLL中的函数时,就会用到函数指针。假设用C写的一段代码如下:
    typedef int (*PVFN)(int)
//定义函数指针类型
    int main()
    {
      HMODULE hModule = LoadLibrary(&quot;test.dll&quot;);
      PVFN pvfn = NULL;
      pvfn = (PVFN) GetProcAddress(hModule, &quot;Function1&quot;);
      pvfn(2);
      FreeLibrary(hModule);
    }
    就我个人感觉来说,C语言中定义函数指针类型的typedef代码的语法有些晦涩,而同样的代码在 Object Pascal中却非常易懂:
    type PVFN = Function (para : Integer) : Integer;
    var fn : PVFN

      //也可以直接在此处定义,如:fn : function (para:Integer):Integer;
      hm : HMODULE;
    begin
      hm := LoadLibrary('test.dll');
      fn := GetProcAddress(hm, 'Function1');
      fn(2);
      FreeLibrary(hm);
    end;

在delphi利用指针构造链表



因为delphi的数据类型使用很方便,我一直以来都没有使用过指针,但最近的一个软件需要使用到链表,所以不得不学习delphi的指针。不用不知道,到用的时候才发现凡是介绍delphi的书籍都回避了指针的问题,好不容易在网上找到一篇《浅谈Object Pascal的指针》。虽然文章那delphi的指针和c/c++的进行了对比学习,但实用例子实在太少了,所以我在写完程序后,把一些使用过程里的感想写下。

现在假设我们的链表存储的只是一个变量 i,那么在c/c++里的结构体定义如下:

struct Ttest

{

int i;

struct Ttest * next;

};

我仿照c的定义方式写了一个结构体

type Ttest = record

i: integer;

next: ^Ttest;

end;

试编译了一次,出错在 next: ^Ttest
上(类型未定义),看来在delphi严格语法下,结构体里不能出现自己的结构体指针,这就让我有些为难了,看来只有想办法对他进行迂回作战。经过再次研究《浅谈Object Pascal的指针》发现有一种无类型指针Pointer可以使用,便把^Ttest替换为Pointer。编译通过了。既然next指针是作为无类型的指针,那在使用时就必须先把这个指针赋值给一个有类型指针后才能对指针所指向的结构体变量进行控制。

有了结构体,接下来,我设计了一个用链表实现的队列的类TQueue。(这里给出的类是简化了的)

Type

TQueue = class

private

First, last: ^Ttest

Public

Procedure add(s: integer);

End;





Procedure TQueue.add(s: integer)

Var

P: ^Ttest;

Begin

Getmam(P, sizeof(Ttest));

p.i := i;

p.next := nil;

last.next := p;

last := p;

End;

Add()是往队列里添加一个数据,它先用getman函数获得一块内存,然后变量赋值,在把新分配的变量挂入链表的末端。看起来很简单,实际写起代码也很简单,不过编译器就是不给你通过。在 last := p 这里提示变量类型不一致。我又重新检查了一遍代码,并为发现又什么问题,都是定义为^Ttext的指针,为什么就是不行!对着屏幕发呆了一会,决定进行迂回作战,这次把first,last都定义为无类型指针Pointer,再次编译通过了。不过需要对add()进行一些相应的改动,添加了一个临时结构体变量t。

Procedure TQueue.add(s: integer)

Var

t, P: ^Ttest;

Begin

Getmam(P, sizeof(Ttest));

p.i := i;

p.next := nil;

t := last;

t.next := p;

last := p;

End;



写完程序后,看到一篇文章说Pchar的准确定义是^char,想想,在我这里是否也能这样定义,这样就不会出现类型不一致的问题。我试着在Ttest的定义后添加一句 Ptest = ^Ttest
并且把first,last和add()里的p都定义为Ptest,这次不需要使用临时变量t就能编译通过。看来delphi的编译还是无法识别在不同两个地方用 ^方式定义的指针,写程序时如果需要经常使用到指针,最好把它独立出来重新定义一下。
 
多人接受答案了。
 
后退
顶部