ele_eye 发表于 2011-8-16 15:22:22

【原创】从 C 角度去看 DELPHI --- delphi指针 和 C一样强劲

当您 由于各变量类型之间不能幅值,频繁的去操作位移,与,或来传递不同类型间的数值时,当您不知道如何把一个23位的整形或浮点数差分为byte数组时,如果您学会了且熟练使用指针,这一切都是举手之劳,这时的你才会觉得天空如此之蓝,才会觉得这才是创作程序,而不是你仅仅堆积和拼凑代码。

1 ---- 使用指针传递缓存参数

像C 一样 uint8_t *pbuf; 来传递缓存首地址,其实delphi一样可以做到,使用pointer 而已不是 array of byte

第一个例子: 缓存拷贝
C
uint8_t memCopy(uint8_t* dstBuf, uint8_t* srcBuf, uint8_t LenOfByte)
{
    for(u8 i=0;i<LenOfByte;i++)
    {
       dstBuf++ = srcBuf++;   
    }
    return LenOfByte;
}

翻译为 delphi
{-------------------------------------------------------------------------------
过程名:    memCopy
说明:                                                               内存拷贝
作者:      ele_eye
日期:      2011.08.11
参数:      dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer
返回值:    DWORD
-------------------------------------------------------------------------------}
function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : ^Byte;
    psrcBuf : ^Byte;
begin
    pdstBuf := dstBuf;
    psrcBuf := srcBuf;

    result := LenOfByte;

    while (LenOfByte > 0) do
    begin
      pdstBuf^ := psrcBuf^;
      Inc(pdstBuf);
      Inc(psrcBuf);
      Dec(LenOfByte);
    end;
end;


使用方法:
C:
uint32_t Vint32 = 0x123456;
uint8_t* ARRint8 = {0};
uint8_t idx = 5;
uint8_t n = memCopy(&Vint32, &(ARRint8));

delphi
var
   s:string;
   buf:array of byte;
   n:integer;
   
   s := '1234567890';
   n := memCopy( @(buf), @(s), length(s));



----------后面整理好再继续

xingliu 发表于 2011-8-16 15:30:03

delphi这个工具做的太经典了。10几年前的电脑运行delphi还是飞快。
微软把delphi专家挖去搞个.net垃圾出来。10几年后的电脑运行起来还是象蜗牛一样。
不服不行。

ele_eye 发表于 2011-8-16 15:43:37

2--- 在串口协议中经常用到的 类型强制转换,以我一小段在XBEE通讯写的程序为例子
C 下位机的C代码:
#pragma pack(1)/* 对齐方式 1 字节 */
typedef struct
{
    u8frmType;
    u64 phyAddr;
    u16 nwkAddr;
    u8RxOption;
}api_rxpkt_hdr_t;
#pragma pack()

把串口接收缓存强制类型转换为帧结构(串口缓存帧结构 和 循环缓存池就不写出来了)
u64 dstPHYAddr = ( (api_rxpkt_hdr_t*)(&rxbuf.buf) ) -> phyAddr;
u16 dstNWKAddr = ( (api_rxpkt_hdr_t*)(&rxbuf.buf) ) -> nwkAddr;



上位机的delphi 代码:
type api_rxpkt_hdr_t = packed record
      frmLen      : api_frmlen_t;
      frmType   : Byte;
      phyAddr   : phyaddr_t;
      nwkAddr   : nwkaddr_t;
      rxOption    : Byte;
    end;
把串口接收缓存强制类型转换为帧结构
var
rxpkt_hdr : ^api_rxpkt_hdr_t;

rxpkt_hdr:= @(rxbuf.buf);
dstPHYAddr := rxpkt_hdr^.phyAddr;
dstNWKAddr := rxpkt_hdr^.nwkAddr;

takashiki 发表于 2011-8-16 16:00:09

从C的角度去看Delphi指针,发现Delphi指针真弱啊,麻烦死了。

比如声明
int *** pppi;         //变态的C指针声明
int* (*ptr)(int, int);//变态的函数指针

Delphi声明麻烦死了,当然,Delphi不容易出错。


另外,楼上的代码
dstPHYAddr := rxpkt_hdr^.phyAddr;
dstNWKAddr := rxpkt_hdr^.nwkAddr;
更易读的的方式应该是
dstPHYAddr := rxpkt_hdr.phyAddr;
dstNWKAddr := rxpkt_hdr.nwkAddr;
比C的要简单不少,也看不见指针满天飞的现象。
两种方法是一样的。Delphi对^限制很少,只在with语句和数组指针中强制要求。

ele_eye 发表于 2011-8-16 16:00:41

3 --- 对DELPHI的一些不解之处

比如在上面例子中 的memCopy函数
如果我写成:
function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : array of Byte;//<--------注意这是定义为数组
    psrcBuf : array of Byte;//<--------注意这是定义为数组
begin
    pdstBuf := dstBuf;
    psrcBuf := srcBuf;

    result := LenOfByte;


    while (LenOfByte > 0) do
    begin
      pdstBuf[(LenOfByte)] := psrcBuf(LenOfByte); {<--------使用 数组+下标 组合方式。方便程序编写,尤其在编写通讯数据帧的时候这种方式比指针方便}
      Dec(LenOfByte);
    end;
end;

当这样使用是没有问题的

dstBuf:array of byte;{<------注意变量类型}
srcBuf:array of byte;
memCopy(@(dstBuf), @(srcBuf), 10);

但是,这样使用也能编译过去,但是执行的时候会出错
var
   s:string;      {<------注意变量类型}
   buf:array of byte;
   n:integer;
   
   s := '1234567890';
   n := memCopy( @(buf), @(s), length(s)); {<-----注意这里是 是@(s)而不是 @(s) 也不是 @s}



-----------------------------------因此就让我想了解 string 类型的 到底是个什么东西(这是delphi一个非常强劲的特色)

ele_eye 发表于 2011-8-16 16:03:38

回复【3楼】takashiki 岚月影
-----------------------------------------------------------------------

各有各的看法 我觉得
dstPHYAddr := rxpkt_hdr^.phyAddr;
dstNWKAddr := rxpkt_hdr^.nwkAddr;
这个方式好 表示变量为指针而非结构-------虽然结构传递的本来就是指针在传递

ele_eye 发表于 2011-8-16 16:17:54

4--- string 字符串 是个什么东西

在delphi的帮助文件中可以很容易的找到:

The String structure contains a string that describes a specific aspect of a file.

String {
    WORD   wLength;
    WORD   wValueLength;
    WORD   wType;
    WCHARszKey[];
    WORD   Padding[];
    String Value[];
} String;


Members

wLength

Specifies the length of the version resource.

wValueLength

Specifies the length of the Value member in the current VS_VERSION_INFO structure. This value is zero if there is no Value member associated with the current version structure.

。。。。。。。。。。。。。。。。。。。。。

看到这个我才明白为什么需要使用 @(s) 来传递字符串内容的首地址 而不是@(s) 也不是 @s

同时也产生了疑问,一般我们定义结构的时候,为了方便继承父类型的数据,定义结构的时候,把数据放在开始,其他放在数据后面
比如:
typedef struct
{
    u8 buf;
    u8 head;
    u8 tail;
    u8 cnt;
}UsartRxBuf_t

这样在使用的时候 &(RxBuf) 和 &(RxBuf.buf) 和 &(RxBuf.buf) 的意义是一样的

我不清楚当初 delphi吧结构定义成这样是为了什么 ,高手来解答解答

takashiki 发表于 2011-8-16 16:26:08

回复【5楼】ele_eye
回复【3楼】takashiki 岚月影
-----------------------------------------------------------------------
各有各的看法 我觉得
dstphyaddr := rxpkt_hdr^.phyaddr;
dstnwkaddr := rxpkt_hdr^.nwkaddr;
这个方式好 表示变量为指针而非结构-------虽然结构传递的本来就是指针在传递
-----------------------------------------------------------------------
Delphi在努力消除指针带来的影响,你是受C语言习惯所左右了,两种写法没什么好与不好之分,都是好的。在Pascal的世界里,不要^是错的,Delphi扩充了语法。


Delphi中的string过于复杂,实在是一言难尽。你就认为他是一个指针好了,指向那个结构体。不信的话,你可以用SizeOf(String)算一下,等于4的。注意,不是Length(string)。话说现在对ShortString(声明为string)类型基本上都没有人用了。

takashiki 发表于 2011-8-16 16:44:10

回复【4楼】 ele_eye

但是,这样使用也能编译过去,但是执行的时候会出错
var
   s:string;      {<------注意变量类型}
   buf:array of byte;
   n:integer;
   
   s := '1234567890';
   n := memCopy( @(buf), @(s), length(s)); {<-----注意这里是 是@(s)而不是 @(s) 也不是 @s}


memCopy是个什么函数啊,你自己写的吗?Delphi中有Move过程,API有CopyMemory,还真没有这个memCopy。


C寓言中:
void * memcpy ( void * destination, const void * source, size_t num );
你是不是把C语言的直接照搬到Delphi中了?

这样声明,
function memcpy(Dest, Src: Pointer; Len: Integer): Integer; cdecl; external 'msvcrt.dll';
调用没有错误啊。注意调用规范,是cdecl,而不是stdcall或默认的register。

ele_eye 发表于 2011-8-16 19:10:11

回复【8楼】takashiki 岚月影
回复【4楼】 ele_eye
但是,这样使用也能编译过去,但是执行的时候会出错
var   
   s:string;      {&lt;------注意变量类型}
   buf:array of byte;   
   n:integer;   
      
   s := '1234567890';   
   n := memcopy( @(buf), @(s), length(s)); {&lt;-----注意这里是 是@(s)而不是 @(s) 也不是 @s}
memcopy是个什么函数啊,你自己写的吗?delphi中有move过程,api有copymemory,还真没有这个memcopy。
c寓言中:
void * memcpy ( void * destination, const void * source, ......
-----------------------------------------------------------------------

memCopy 是我在LZ位自己写的程序,用于测试内存拷贝的一个例子

xivisi 发表于 2011-8-16 19:12:46

真怀念 高中的时候Pascal 参加竞赛

takashiki 发表于 2011-8-16 20:49:46

回复【9楼】ele_eye

你这个程序没有错误的,只是根据编译环境的不同结果不同。

<=D2007: Buf = { $31, $32, ... $30, x, x, x, ...}   //x表示不确定,string是ANSI型的
=Delphi.Net,Delphi Prism: 直接Error,无法编译       //.Net版Delphi不支持指针
>=D2009:Buf = { $31, $00, $32, $00, $33, $00, ... $30, $00, x, x, ...}      //D2009以上string是Unicode版的

knight_avr 发表于 2011-8-16 21:08:21

回复【11楼】takashiki 岚月影
-------------------------------------------------------------------

呵呵 非常感谢 岚月影


你看一下 LZ位和 4楼 两个 memCopy 的程序是不一样的

4楼的程序 即使是对 也不是很严谨,在我调用这个程序,处理数组指针的时候是没有问题,但是如果传入的是字符串指针的时候,运行阶段就会报错


LZ位的memCopy是完全正确的,任何指针都是可以的

---------我在用 DELPHI7

takashiki 发表于 2011-8-16 22:35:22

回复【12楼】knight_avr
回复【11楼】takashiki 岚月影
-------------------------------------------------------------------
呵呵 非常感谢 岚月影
你看一下 lz位和 4楼 两个 memcopy 的程序是不一样的
4楼的程序 即使是对 也不是很严谨,在我调用这个程序,处理数组指针的时候是没有问题,但是如果传入的是字符串指针的时候,运行阶段就会报错
lz位的memcopy是完全正确的,任何指针都是可以的
---------我在用 delphi7

-----------------------------------------------------------------------

脑子懵了,看后面就忘了前面的了,不知道在想啥了,楼主的两个memcopy函数都有问题,只是第一个结果是正确的,第二个不正确而已。


LZ位的程序,与C语言程序并不等价:
function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : ^Byte;
    psrcBuf : ^Byte;
begin
    pdstBuf := dstBuf;                         //与C语言版本相比,多出了两个额外的赋值过程。致使以后的操作均不针对dstBuf,srcBuf进行操作,与C语言版行为完全不同。但结果是一致的。
    psrcBuf := srcBuf;

    result := LenOfByte;

    while (LenOfByte > 0) do
    begin
      pdstBuf^ := psrcBuf^;
      Inc(pdstBuf);
      Inc(psrcBuf);
      Dec(LenOfByte);
    end;
end;


改写如下:
function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : ^Byte absolute dstBuf;         //Delphi语言强大的引用,却基本没几个人用
    psrcBuf : ^Byte absolute srcBuf;
begin
    result := LenOfByte;

    while (LenOfByte > 0) do
    begin
      pdstBuf^ := psrcBuf^;
      Inc(pdstBuf);
      Inc(psrcBuf);
      Dec(LenOfByte);
    end;
end;


或者直接声明为
function MemCopy(dstBuf, srcBuf: PByte;LenOfByte:Integer):DWORD;






function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : array of Byte;//<--------注意这是定义为数组
    psrcBuf : array of Byte;//<--------注意这是定义为数组
begin
    pdstBuf := dstBuf;
    psrcBuf := srcBuf;

    result := LenOfByte;


    while (LenOfByte > 0) do
    begin
      ///////////注意下面一行,数组越界了。Delphi默认数组下标从0开始,当然可以自己改动。动态数据存在潜在的风险,不过我用这么久Delphi,它的动态数组确实是从0开始的。
      pdstBuf[(LenOfByte)] := psrcBuf(LenOfByte); {<--------使用 数组+下标 组合方式。方便程序编写,尤其在编写通讯数据帧的时候这种方式比指针方便}
      Dec(LenOfByte);
    end;
end;

改写一下:
function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : array of Byte absolute dstBuf;//<--------注意这是定义为数组
    psrcBuf : array of Byte absolute srcBuf;//<--------注意这是定义为数组
begin
    result := LenOfByte;   

    while (LenOfByte > 0) do
    begin
      Dec(LenOfByte);                                    //<======== 提到前面去了,这样就不会越界了
      pdstBuf := psrcBuf;            //<======== 数组成员访问是用中括号的
    end;
end;

ele_eye 发表于 2011-8-17 09:55:04

回复【13楼】takashiki 岚月影
-----------------------------------------------------------------------

1 -----------------------
不错 还是你的delphi功底好 学到一招pdstBuf : ^Byte absolute dstBuf;         //Delphi语言强大的引用,却基本没几个人用
absolute确实不知道还有这个用法,新学到了一个技巧


2----------------------------------
这一句 pdstBuf := psrcBuf;是临时在记事本里面写的,没有去验证,不好意思

以前在delphi中验证的时候应该是使用for循环的 ,测试结果和我在四楼提到的一样
    for i:=0 to LenOfByte-1 do
    begin      
      pdstBuf := psrcBuf;            //<======== 数组成员访问是用中括号的
    end;         

3-----------------------------------
或者直接声明为
function MemCopy(dstBuf, srcBuf: PByte;LenOfByte:Integer):DWORD;
pbyte 虽然是指针,但已经限定为Byte的指针,通用性能不强,还是用pointer的好

snail0204 发表于 2011-8-17 10:12:09

mark

sdjiangyi 发表于 2011-8-17 21:07:03

让我来回答你吧

delphi比c++高级。 因为delphi有诸多新特性, 引用计数、内存自动管理即是其中一个方面。

delphi引入了数组的概念, 这里数组是真正的数组, 可以通过Length函数取出数组元素个数。 这个和c++的数组有本质区别, c++的数组其实就是指针。 指针之间可以互相赋值。
delphi的数组绝不仅仅是一个指针而已,每一个数组都在delphi内存管理器里进行注_册, 有唯一地址和引用计数, 如果两个数组不幸有了同一个地址,而且是以转换成数字形式进行的赋值, 如:
function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;
var
    pdstBuf : array of Byte;//<--------注意这是定义为数组
    psrcBuf : array of Byte;//<--------注意这是定义为数组

那么,pdstbuf在自动被销毁的时候, 内存管理器干掉了这块内存, 不幸的的是, 这个内存还有用呢。。。。

数组->指针形式->赋予其他数组。 就是上面这个悲剧。这个指针形式,就是dstBuf:Pointer这个参数。

数组->赋予其他数组, 没问题, 因为内存管理器识别了这块内存被两个变量引用了, 一个变量被销毁, 不要紧, 还有一个在引用这
块内存, 内存不会被销毁, 直到第二个变量也被销毁, 没有任何变量使用这个内存了, 那么这个内存就真正被销毁了。

另外, delphi内存出错了, 不一定会弹出错误对话框, 可能程序还在运行,而且似乎没有问题!!!!直到偶尔蹦出一个你根本无法理解的错误为止,或者运行到最后也不报错。但是,错误就是错误,必须避免。

从根本上记住, delphi的数组, 和c++的数组完全就是两种东西,就行了。 c++的数组, 就是一个指针指向的一块内存。delphi的数组, 是在内存管理器里登记的一块内存, 它 的数据类型、元素个数、有多少变量在引用它,都被登记了。销毁方式也不一样。

sdjiangyi 发表于 2011-8-17 21:22:24

absolute这个关键字,已经很少很少被使用了,基于代码通用的原则。 有其他替代方式。

另外,
楼主写的memCopy是多余的, delphi内置了好几个内存copy的函数和过程。我常用的是内嵌的move过称:

procedure       Move( const Source; var Dest; count : Integer );


另外,
var
   s:string;      {<------注意变量类型}
   buf:array of byte;
   n:integer;
   
   s := '1234567890';
   n := memCopy( @(buf), @(s), length(s)); {<-----注意这里是 是@(s)而不是 @(s) 也不是 @s}

可以改为:

var
   buf:array of byte;
   integer( ( @buf )^ ) := $12345678 ;

这叫做强制存储。 和强制转换类似, 强制转换是读取, 强制存储是强制以某数据类型的方式写入。很常用的功能。

另外,还有
var
   buf:array of byte;
   vd: ^integer = @buf;
   
   vd^ := $12345678 ;

sdjiangyi 发表于 2011-8-17 21:33:58

function memCopy(dstBuf:Pointer; srcBuf:Pointer; LenOfByte:Integer):DWORD;   
var   
    pdstBuf : array of Byte absolute dstBuf;//<--------注意这是定义为数组
    psrcBuf : array of Byte absolute srcBuf;//<--------注意这是定义为数组

这种用法,是绝对的错误, 不要这样用数组。 delphi里, 数组不是一个单纯的指针!因为有引用计数的问题!

数组, 声明后, 申请空间, 赋值、使用,销毁。老老实实的,就这个流程。
顶多可以 :
type
   ta = array of byte ;
a : ta ;
b : ta ;

后面某个地方:
a:=b;

这样没有问题。 这是唯一一个合理的其他用法了!!!

sdjiangyi 发表于 2011-8-17 21:46:04

/*
Delphi中的string过于复杂,实在是一言难尽。你就认为他是一个指针好了,指向那个结构体。不信的话,你可以用SizeOf(String)算一下,等于4的。注意,不是Length(string)。话说现在对ShortString(声明为string)类型基本上都没有人用了。
*/


这个说法是错误的, string属于一种动态数组。不需要申请空间的动态数组!!不需要释放的动态数组!!!一种拿来就用的动态数组,可以随便互相赋值的数组。也可以强制申请空间的数组。

xrwf_2009 发表于 2011-8-17 22:21:25

mark

ele_eye 发表于 2011-8-18 10:18:07

回复【19楼】sdjiangyi
/*
delphi中的string过于复杂,实在是一言难尽。你就认为他是一个指针好了,指向那个结构体。不信的话,你可以用sizeof(string)算一下,等于4的。注意,不是length(string)。话说现在对shortstring(声明为string)类型基本上都没有人用了。
*/
这个说法是错误的, string属于一种动态数组。不需要申请空间的动态数组!!不需要释放的动态数组!!!一种拿来就用的动态数组,可以随便互相赋值的数组。也可以强制申请空间的数组。

-----------------------------------------------------------------------

非常感谢 sdjiangyi的精彩论述

在此我也自乐一下 ---- 抛砖引玉 我的目的做到了 哈哈

我一直使用DELPHI写程序 但是怎么用都有点觉得别扭 尤其是 string 这个东东 现在有点明白了 再次非常感谢!

bigchn 发表于 2011-8-18 10:37:50

还好, 还有几个死不改悔的同类。
请教还在用Delphi的朋友们,你们现在用什么版本的Delphi做软件,我这边至今仍在用Delphi6开发。
用这个工具给客户开发软件,大多数客户不知道是什么,只有老外一般都知道这个,不会啰嗦----许多时候交付软件给客户时,只能说是VC++或C#什么得做的,除非他们要源代码,但多数时候是不会提供的,除非合同明确。

ele_eye 发表于 2011-8-18 11:27:40

dephi7才200M多5年前买的单核赛扬2.0的电脑都跑的很开心

不想换了

神马 2009 2010 都是浮云 不但编译软件垃圾 费电脑资源,而且编写出来的软件都慢的一台糊涂

imjacob 发表于 2011-8-18 12:49:49

mark

sdjiangyi 发表于 2011-8-19 08:31:13

回复【21楼】ele_eye

-----------------------------------------------------------------------

再有什么问题,可以问我, 我qq627216360 。
页: [1]
查看完整版本: 【原创】从 C 角度去看 DELPHI --- delphi指针 和 C一样强劲