搜索
bottom↓
回复: 25

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

[复制链接]

出0入0汤圆

发表于 2011-8-16 15:22:22 | 显示全部楼层 |阅读模式
当您 由于各变量类型之间不能幅值,频繁的去操作位移,与,或来传递不同类型间的数值时,当您不知道如何把一个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[100] = {0};
  uint8_t idx = 5;
  uint8_t n = memCopy(&Vint32, &(ARRint8[i*4]));

delphi
var
   s:string;
   buf:array[0..99] of byte;
   n:integer;
   
   s := '1234567890';
   n := memCopy( @(buf[0]), @(s[1]), length(s));

  

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

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2011-8-16 15:30:03 | 显示全部楼层
delphi这个工具做的太经典了。10几年前的电脑运行delphi还是飞快。
微软把delphi专家挖去搞个.net垃圾出来。10几年后的电脑运行起来还是象蜗牛一样。
不服不行。

出0入0汤圆

 楼主| 发表于 2011-8-16 15:43:37 | 显示全部楼层
2--- 在串口协议中经常用到的 类型强制转换,以我一小段在XBEE通讯写的程序为例子
C 下位机的C代码:
#pragma pack(1)  /* 对齐方式 1 字节 */
typedef struct
{
    u8  frmType;
    u64 phyAddr;
    u16 nwkAddr;
    u8  RxOption;
}api_rxpkt_hdr_t;  
#pragma pack()

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

出0入93汤圆

发表于 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语句和数组指针中强制要求。

出0入0汤圆

 楼主| 发表于 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[0..99] of byte;  {<------注意变量类型}
srcBuf:array[0..99] of byte;
memCopy(@(dstBuf[0]), @(srcBuf[0]), 10);

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



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

出0入0汤圆

 楼主| 发表于 2011-8-16 16:03:38 | 显示全部楼层
回复【3楼】takashiki 岚月影
-----------------------------------------------------------------------

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

出0入0汤圆

 楼主| 发表于 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;
    WCHAR  szKey[];
    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[1]) 来传递字符串内容的首地址 而不是@(s[0]) 也不是 @s

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

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

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

出0入93汤圆

发表于 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[n])类型基本上都没有人用了。

出0入93汤圆

发表于 2011-8-16 16:44:10 | 显示全部楼层
回复【4楼】 ele_eye

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

出0入0汤圆

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

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

出0入0汤圆

发表于 2011-8-16 19:12:46 | 显示全部楼层
真怀念 高中的时候  Pascal 参加竞赛

出0入93汤圆

发表于 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版的

出0入0汤圆

发表于 2011-8-16 21:08:21 | 显示全部楼层
回复【11楼】takashiki 岚月影
-------------------------------------------------------------------

呵呵 非常感谢 岚月影


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

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


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

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

出0入93汤圆

发表于 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[LenOfByte] := psrcBuf[LenOfByte];            //<======== 数组成员访问是用中括号的
    end;  
end;

出0入0汤圆

 楼主| 发表于 2011-8-17 09:55:04 | 显示全部楼层
回复【13楼】takashiki 岚月影
-----------------------------------------------------------------------

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


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

以前在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的好

出0入0汤圆

发表于 2011-8-17 10:12:09 | 显示全部楼层
mark

出0入0汤圆

发表于 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的数组, 是在内存管理器里登记的一块内存, 它 的数据类型、元素个数、有多少变量在引用它,都被登记了。销毁方式也不一样。

出0入0汤圆

发表于 2011-8-17 21:22:24 | 显示全部楼层
absolute这个关键字,已经很少很少被使用了,基于代码通用的原则。 有其他替代方式。

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

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


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

可以改为:

var
   buf:array[0..99] of byte;  
   integer( ( @buf[0] )^ ) := $12345678 ;

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

另外,还有
var
   buf:array[0..99] of byte;  
   vd: ^integer = @buf[0];
   
   vd^ := $12345678 ;

出0入0汤圆

发表于 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  ;

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

出0入0汤圆

发表于 2011-8-17 21:46:04 | 显示全部楼层
/*
Delphi中的string过于复杂,实在是一言难尽。你就认为他是一个指针好了,指向那个结构体。不信的话,你可以用SizeOf(String)算一下,等于4的。注意,不是Length(string)。话说现在对ShortString(声明为string[n])类型基本上都没有人用了。
*/


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

出0入0汤圆

发表于 2011-8-17 22:21:25 | 显示全部楼层
mark

出0入0汤圆

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

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

非常感谢 sdjiangyi  的精彩论述  

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

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

出0入0汤圆

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

出0入0汤圆

 楼主| 发表于 2011-8-18 11:27:40 | 显示全部楼层
dephi7  才200M多  5年前买的单核赛扬2.0的电脑都跑的很开心

不想换了  

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

出0入0汤圆

发表于 2011-8-18 12:49:49 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-19 08:31:13 | 显示全部楼层
回复【21楼】ele_eye

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

再有什么问题,可以问我, 我qq  627216360 。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-22 22:15

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表