浅谈Object Pascal的指针 (100分)

  • 主题发起人 主题发起人 Nicrosoft
  • 开始时间 开始时间
N

Nicrosoft

Unregistered / Unconfirmed
GUEST, unregistred user!
浅谈Object Pascal的指针

  Nicrosoft(nicrosoft@sunistudio.com) —— 2001.8.26
http://www.sunistudio.com/nicrosoft/
  东日文档:http://www.sunistudio.com/asp/sunidoc.asp

  大家都认为,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 19do
      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, "teststr");
    strcat(str, "_testok");
    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("test.dll");
      PVFN pvfn = NULL;
      pvfn = (PVFN) GetProcAddress(hModule, "Function1");
      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;
 
怎么还给分?
 
我要分噢, 快给几分吧 ^_^。
to Nicrosoft:
你是否就是 csdn 上的 Nicrosoft?
 
我也要分。
 
没错他就是CSDN上的奈软!
 
真是大好人!
谢了!。。。
 
谢谢奈兄!!!
 
还有没有类似的文章?多发几篇吧!谢了!:)
 
1.Pimage=^TImage;
2.P:pointer;
3.算了,你都会呀
 
呵呵 来点分分哟
 
Object Pascal的动态数组与C/C++的很有些不同,我在昨天做的一个程序里遇到,觉得很是
恼人:我要把集合了若干个结构(record/struct)的一个动态数组通过流写到一个文件中,
用Delphi将动态数组写到文件中,如果是FileStream.WriteBuffer(动态数组名,动态数组长度)
和ReadBuffer来读写就会失败,不是指的读写操作失败,而是写入和读出的数据是错误的,
但是如果在循环中依次写入和读出该动态数组的各个元素的方法又能成功。
而用CB时,由于动态数组是用calloc分配的(没有用DynamicArray模块类,用了也失败),
指向动态数组的数据被一次就写入和读出了,没有遇到任何问题。
我就觉得纳闷,为什么Delphi一次写入整个动态数组就不能成功,而依次写入其各元素就
能成功,难道它在内存中的储存位置不是连继排列的吗?
 
好人啊!
 
[:)][:)]
在CSDN上看过了
注意排版
 
敬仰之余,拙见如下:
第七节Pascal程序注释中
“        //在C中,数组的名称可以直接作为数组首地址指针来用
        //但Pascal不是这样的,因此 str前要加上取地址的运算符

提法不妥,实质上在OOP中也可以用变量名代替首指针,只是Str函数没有那样封装而已,
请看:
function MyStrCopy(var Dest;
Source: PChar): PChar;
begin
StrCopy(@Dest, Source);
end;

重新封装一下,你就可以
myStrCopy(str, 'teststr');
甚至可以
myStrCopy(str[3], 'teststr');
第八节这样定义也可以运行
    type TVFN = Function (para : Integer) : Integer;
    var fn : TVFN;
      //也可以直接在此处定义,如:fn : function (para:Integer):Integer;
      hm : HMODULE;
    begin
      hm := LoadLibrary('test.dll');
      @fn := GetProcAddress(hm, 'Function1');
      fn(2);
      FreeLibrary(hm);
    end;

呵呵,Pacsal的灵活性实在比C还高
 
对初学者而言很不错的教材!
 
还有,记录指针在Delphi下和以前的Pascal有所不同(Delphi 5 vs BP7)
比如:
PMyRec = ^TMyRec;
TMyRec = record
Code: string;
Name: string;
end;

var
P: PMyRec;
New(P);
P.Code := '001';
<---------- 这两句在BP7下是非法的,但是在Delphi 5下是合法的,
P.Name := 'softdog';
<---------- Delphi会认为你是调用 P^.Code和P^.Name
……
 
呵呵,Nicrosoft跑大富翁布道来了。
 
后退
顶部