电竞比分网-中国电竞赛事及体育赛事平台

分享

Delphi指針類型淺析(附件)

 quasiceo 2014-10-01
2010-04-26 18:12 448人閱讀 評論(0) 收藏 舉報

目錄(?)[+]

附件資料
*指針的使用(代碼)

示例:簡單的指針應(yīng)用

代碼:

procedure TForm1.Button2Click(Sender: TObject);

var

    a: Integer;

    p: ^Integer;

begin

    with self.Memo1.Lines do

    begin

        a := 100;

        Add('數(shù)據(jù)內(nèi)容:' + inttostr(a));

        //---

        p := @a;

        Add('數(shù)據(jù)地址:' + inttostr(Integer(p)));

        //---

        Add('數(shù)據(jù)地址指向的內(nèi)容:' + inttostr(p^));

    end;

end;

 

procedure TForm1.Button2Click(Sender: TObject);

type

    PInteger = ^Integer;

    PPInteger = ^PInteger;

var

    a: Integer;

    p: PInteger;

    pp: PPInteger;

begin

    with self.Memo1.Lines do

    begin

        a := 100;

        Add('Integer數(shù)據(jù)內(nèi)容:' + inttostr(a));

        //---

        p := @a;

        Add('Integer指針內(nèi)容:' + inttostr(Integer(p)));

        Add('Integer指針指向的數(shù)據(jù):' + inttostr(p^));

        //---

        pp := @p;

        Add('PInteger指針內(nèi)容:' + inttostr(Integer(pp)));

        Add('PInteger指針指向的數(shù)據(jù):' + inttostr(Integer(pp^)));

    end;

end;

 

*指針的使用(匯編)

示例:簡單的指針應(yīng)用

說明:

對于編譯器來說,指針的類型可以用來標(biāo)明地址所指向區(qū)域的大小、所指向的類型(整型、對象、方法),以及進(jìn)行指針運(yùn)算時指針偏移的長度。對于有類型指針的操作最終反映到編譯器的可執(zhí)行代碼中,如果不參考可執(zhí)行代碼,單憑一個內(nèi)存地址,我們并無法判斷地址所指向的數(shù)據(jù)類型。

代碼:

procedure TForm1.Button2Click(Sender: TObject);

var

    a:array[0..1] of Integer;

    b: Integer;

    p: ^Integer;

begin

    a[0] := 1;

    a[1] := 2;

    //---

    p := @a[0];

    b := p^;

    //---

    Inc(p);

    b := p^;

end;

匯編:

編譯器根據(jù)源代碼中聲明的變量類型分配內(nèi)存如下:

[ebp - $04]存儲為self,大小為4字節(jié),因為self聲明為對象指針

[ebp - $08]標(biāo)識為a[1],大小為4字節(jié),因為a[0]聲明為Integer

[ebp - $0c]標(biāo)識為a[0]

[ebp - $10]標(biāo)識為b,大小為4字節(jié),因為b聲明為Integer

[ebp - $14]標(biāo)識為p,大小為4字節(jié),因為p聲明為Integer指針

[ebp - $18]存儲為Sender,大小為4字節(jié),因為Sender聲明為對象指針

其中“ebp - $04為內(nèi)存地址,“[ebp - $04]”為地址所指向的數(shù)據(jù),ebpeax32位寄存器可以用于存取一個4字節(jié)數(shù)據(jù)。

 

 

 

代碼:

procedure TForm1.Button2Click(Sender: TObject);

var

    a:array[0..1] of Byte;

    b: Byte;

    p: ^Byte;

begin

    a[0] := 1;

    a[1] := 2;

    //---

    p := @a[0];

    b := p^;

    //---

    Inc(p);

    b := p^;

end;

匯編:

編譯器根據(jù)源代碼中聲明的變量類型分配內(nèi)存如下:

[ebp - $04]存儲為self,大小為4字節(jié),因為self聲明為對象指針

[ebp - $05]標(biāo)識為a[1],大小為4字節(jié),因為a[0]聲明為Integer

[ebp - $06]標(biāo)識為a[0]

[ebp - $07]標(biāo)識為b,大小為4字節(jié),因為b聲明為Integer

[ebp - $0c]標(biāo)識為p,大小為4字節(jié),因為p聲明為Integer指針

[ebp - $10]存儲為Sender,大小為4字節(jié),因為Sender聲明為對象指針

其中編譯器為了實現(xiàn)數(shù)據(jù)對齊,所以申請了大小為$10的空間。

 

 

*復(fù)雜數(shù)據(jù)類型指針(代碼)

示例:字符串指針

代碼:

procedure TForm1.Button2Click(Sender: TObject);

type

    buffer = string[255];

    ptr = ^buffer;

var

    b1: buffer;

    b2: ptr;

begin

    with self.Memo1.Lines do

    begin

        b1 := 'zhang';

        Add('數(shù)據(jù)內(nèi)存大小:' + IntToStr(SizeOf(b1)));

        Add('數(shù)據(jù)內(nèi)容大?。?/span>' + IntToStr(PByte(@b1)^));

        //---

        b2 := @b1;

        Add('指針大小:' + IntToStr(SizeOf(b2)));

        Add('指針指向數(shù)據(jù)內(nèi)存大?。?/span>' + IntToStr(SizeOf(b2^)));

    end;

end;

說明:

首先,ptr是一個指針類型,而b2是這個指針類型的變量。

其次,ptr是一個指向255長度的短字符串類型的指針,加上一個看不見的計數(shù)字節(jié),共256B。當(dāng)ptr沒有分配內(nèi)存空間,也沒初始化時指向的地址是隨機(jī)的,隨意引用可能會出問題。但是只要它指向了某個地址,就代表它指向了256B的空間,它可以指向任何有效的字符串變量,但是無論被指向的字符串有多長,b2^的長度始終是256。

 

*記錄指針(代碼)

示例:記錄指針的應(yīng)用

代碼:

procedure TForm1.Button2Click(Sender: TObject);

type

    PTMyRecord = ^TMyRecord;

    TMyRecord = record

        Data1:Integer;

        Data2:Integer;

    end;

var

    ARecord: TMyRecord;

    pRecord: PTMyRecord;

begin

    pRecord := @ARecord;

    ARecord.Data1 := 11;

    pRecord^.Data2 := 22;

    pRecord.Data2 := 33;

end;

 

*過程指針(代碼)

示例:方法指針的結(jié)構(gòu)

說明:

1)、過程/函數(shù)指針是一個32位的指針

代碼:

procedure TForm1.Button2Click(Sender: TObject);

type

    TMyMethod = function(X: Integer): Integer;

var

    F: TMyMethod;

begin

    Self.Memo1.Lines.Add('函數(shù)指針大小:' + IntToStr(SizeOf(TMyMethod)));

end;

 

示例:過程指針的應(yīng)用

說明:

1)、通過賦值語句“過程變量 := 過程”,使得過程變量指向過程或者函數(shù)入口地址。

2)、在賦值語句“過程變量 := 過程”中,左邊變量的類型決定了右邊的過程或者方法指針解釋。

3)、可以使用過程變量引用聲明的過程或者函數(shù)。

4)、無論何時一個過程變量(procedural variable)出現(xiàn)在一個表達(dá)式中,它表示調(diào)用所指向的函數(shù)或者過程。

5)、對于過程變量P, @PP轉(zhuǎn)換成一個包含地址的無類型的指針變量(即@P等價于無類型指針P),此時可以把一個無類型的指針值賦給過程變量P。

代碼:

function SomeFunction(X: Integer): Integer;

begin

    ShowMessage(IntToStr(X));

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F: function(X: Integer): Integer;

    I: Integer;

begin

    F := SomeFunction; //--f賦值

    I := F(4);         //--調(diào)用所指向的函數(shù)

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F: function(X: Integer): Integer;

    I: Integer;

begin

    F := SomeFunction; //--f賦值

    I := F(4); //--調(diào)用所指向的函數(shù)

    //---

    Self.Memo1.Lines.Add('函數(shù)指針大小' + IntToStr(SizeOf(@SomeFunction)));

end;

 

示例:過程指針的應(yīng)用

代碼:

function SomeFunction: Integer;

begin

    Result := 0;

    ShowMessage('0');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F: function: Integer;

    P:Pointer;

begin

    P := @SomeFunction; //--P賦值

    @F := P; //--f賦值

    F;

end;

說明:

可以使用@操作符,把一個無類型的指針值賦給一個過程變量,如調(diào)用“@過程變量 := 無類型指針值”,此時過程變量指向這個值。

 

示例:過程指針的應(yīng)用

代碼:

function SomeFunction: Integer;

begin

    Result := 0;

    ShowMessage('0');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F,G: function: Integer;

    I: Integer;

begin

    F := SomeFunction; //--F賦值

    G := F; //--F的值拷貝給G

    I := G; //--調(diào)用函數(shù)

end;

說明:

第一句獲得函數(shù)的入口,第二句將指針復(fù)制,第三句獲得函數(shù)的返回值。 

 

示例:過程指針的函數(shù)調(diào)用

代碼:

function SomeFunction: Integer;

begin

    Result := 0;

end;

 

function MyFunction: Integer;

begin

    Result := 0;

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F: function: Integer;

    I: Integer;

begin

    F := SomeFunction; //--f賦值

    if F = MyFunction then

        ShowMessage('比較的函數(shù)返回值得結(jié)果');

end;

說明:

   if語句中,F的出現(xiàn)導(dǎo)致一個函數(shù)調(diào)用;編譯器調(diào)用F指向的函數(shù),然后調(diào)用Myfunction,比較結(jié)果。這個規(guī)則是無論何時一個過程變量出現(xiàn)在一個表達(dá)式中,它表示調(diào)用所指向的函數(shù)或者過程。有時F指向一個過程(沒有返回值),或者f指向一個需要參數(shù)的函數(shù),則前面的語句會產(chǎn)生一個編譯錯誤。

 

示例:比較過程入口地址

代碼:

function SomeFunction: Integer;

begin

    Result := 0;

end;

 

function MyFunction: Integer;

begin

    Result := 0;

    ShowMessage('123');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F: function: Integer;

begin

    F := MyFunction; //--f賦值

    if @F = @MyFunction then

        ShowMessage('比較函數(shù)地址');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    F: function: Integer;

    P:Pointer;

begin

    F := MyFunction; //--f賦值

    P := @F;

    if P = @MyFunction then

        ShowMessage('比較函數(shù)地址');

end;

說明:

@FF轉(zhuǎn)換成一個包含地址的無類型的指針變量,@myfunction返回myfunction的地址。

 

*方法指針(代碼)

示例:方法指針的結(jié)構(gòu)

說明:

1)、方法指針指向一個結(jié)構(gòu),其結(jié)構(gòu)如下

 TMethod = record

  Code: Pointer;//指向過程/函數(shù)的指針

     Data: Pointer;//指向?qū)ο蟮闹羔?/span>

 end;

代碼:

type

    TMyObject = class(TObject)

    public

        procedure Method1(Sender: TObject);

    end;

 

procedure TMyObject.Method1(Sender: TObject);

begin

    ShowMessage('123');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

type

    TMyMethod = procedure(Sender: TObject) of object;

var

    AMyMethod: TMyMethod;

    AMyObject: TMyObject;

begin

    with self.Memo1.Lines do

    begin

        Add('方法指針大小:' + IntToStr(SizeOf(TMyMethod)));

        //---

        AMyObject := TMyObject.Create;

        try

            Add('對象指針:' + IntToHex(Integer(AMyObject),2));

            Add('對象方法指針:' + IntToHex(Integer(@TMyObject.Method1),2));

            //---

            AMyMethod := AMyObject.Method1;

            //---

            with TMethod(AMyMethod) do

            begin

                Add('對象指針:' + IntToHex(Integer(Data),2));

                Add('對象方法指針:' + IntToHex(Integer(Code),2));

            end;

            //---

            AMyMethod(AMyObject);

        finally

            AMyObject.Free;

        end;

    end;

end;

 

示例:方法指針的結(jié)構(gòu)

代碼:

type

    TMyObject = class(TObject)

    public

        procedure TestMethod(Value: Integer); virtual;

    end;

    TMyObject1 = class(TMyObject)

    public

        procedure TestMethod(Value: Integer); override;

    end;

 

procedure TMyObject.TestMethod(Value: Integer);

begin

    ShowMessage('TMyObject:' + inttostr(Value));

end;

 

procedure TMyObject1.TestMethod(Value: Integer);

begin

    inherited;

    ShowMessage('TMyObject1:' + inttostr(Value));

end;

 

procedure TForm1.Button2Click(Sender: TObject);

type

    TWndMethod = procedure(Value: Integer) of object;

var

    SomeMethod: TWndMethod;

    AMyObject: TMyObject1;

    Msg: TMessage;

begin

    AMyObject := TMyObject1.Create;

    try

        SomeMethod := AMyObject.TestMethod; //--賦值后SomeMethod包含TestMethod AMyObject的指針

        //SomeMethod := TMyObject1.TestMethod;  //--錯誤!不能用類引用。

        SomeMethod(1); //--執(zhí)行方法

    finally

        AMyObject.Free;

    end;

end;

說明:

TWndMethod 是一種對象方法類型,它指向一個接收Integer) 類型參數(shù)的過程,但它不是一般的靜態(tài)過程,它是對象相關(guān)(object related)的。TWndMethod 在內(nèi)存中存儲為一個指向過程的指針和一個對象的指針,所以占用8個字節(jié)。TWndMethod類型的變量必須使用已實例化的對象來賦值。

如果把 TWndMethod變量賦值給虛方法,這時,編譯器實現(xiàn)為 SomeMethod 指向AMyObject對象虛方法表中的TestMethod 過程的地址和AMyObject對象的地址。也就是說編譯器正確地處理了虛方法的賦值。調(diào)用 SomeMethod(1) 就等于調(diào)用AMyObject.TestMethod (Message)

在可能被賦值的情況下,對象方法最好不要設(shè)計為有返回值的函數(shù)(function),而要設(shè)計為過程(procedure)。原因很簡單,把一個有返回值的對象方法賦值給TWndMethod 變量,會造成編譯時的二義性。

 

示例:方法指針指向?qū)ο蠓椒?/span>

代碼:

type

    TMyObject = class(TObject)

    public

        procedure Method1(Sender: TObject);

    end;

 

procedure TMyObject.Method1(Sender: TObject);

begin

    ShowMessage('123');

end;

 

procedure TForm1.Button2Click(Sender: TObject);

type

    TMyMethod = procedure(Sender: TObject) of object;

var

    AMyMethod: TMyMethod;

    AMyObject: TMyObject;

begin

    AMyObject := TMyObject.Create;

    try

        AMyMethod := AMyObject.Method1;

        AMyMethod(AMyObject);

    finally

        AMyObject.Free;

    end;

end;

 

示例:方法指針指向過程/函數(shù)

說明:

1)、不能使用賦值語句“對象方法指針 =  @過程/函數(shù) ,因為對象方法指針和過程指針(“@過程/函數(shù)”返回一個過程指針)類型不同,它們各自的空間大小也不同,普通的過程指針不包含對象指針,所以不能用于給對象方法指針類型賦值。

2)、因為Delphi中給對象方法指針賦值必須加上對象實例,如TestObj.Hello才行,一種投機(jī)的方法是可以通過TMethod將對象方法指針重定向到一個全局函數(shù)

3)、由于對象方法包括一個隱含對象指針參數(shù)Self,所以定義過程/函數(shù)時必須加入一個假參數(shù)const pSelf: Pointer以保證對象指針self能夠被傳入。

代碼:

type

    TMyObject = class(TObject)

    private

        FTestEvent: TNotifyEvent;

    public

        procedure ExecTestEvent(Sender: TObject);

        //---

    published

        property TestEvent: TNotifyEvent read FTestEvent write FTestEvent;

    end;

 

//--類方法隱藏了第一個參數(shù)為對象的 Self (放在EAX中傳遞)

//--故第一個參數(shù)為 Self: TObject,第二個參數(shù)對應(yīng) TNotifyEvent 的參數(shù)

procedure Gproc(Self: TObject; Sender: TObject);

begin

    ShowMessage('Test');

end;

 

procedure TMyObject.ExecTestEvent(Sender: TObject);

begin

    if Assigned(FTestEvent) then

        FTestEvent(Sender);

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    M: TMethod; //--方法記錄

    AMyObject: TMyObject;

begin

    with M do

    begin

        Code := @Gproc; //--指向方法(這里是全局過程)地址

        Data := nil; //--指向類實例(該值對應(yīng)著 Gproc 調(diào)用時的 Self 參數(shù)值,可為任意值,這里傳nil

    end;

    //---

    AMyObject := TMyObject.Create;

    try

        AMyObject.TestEvent := TNotifyEvent(M);

        AMyObject.ExecTestEvent(nil);

    finally

        AMyObject.Free;

    end;

end;

說明:

1)、第一個參數(shù)Self: TObject;絕對不是多余的,雖然你不用它編譯也能通過,還可能執(zhí)行正確,但如果你想在過程中調(diào)用對象之真的話,沒有它可不行。

Delphi默認(rèn)使用寄存器加堆棧的方式傳遞過程參數(shù),三個參數(shù)以內(nèi)使用EAX、EDX、ECX的順序保存參數(shù),三個以后用堆棧。對類的方法來說,EAX恒定是類實例指針Self,這樣Sender參數(shù)實際上放在EDX中做為第二個參數(shù)傳遞,如下面的代碼:

procedure MyMouseDown(Self: TObject; Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  ShowMessage(Format('Self: %d, Sender: %d, X: %d, Y: %d', [Integer(Self),

    Integer(Sender), X, Y]));

end;

procedure TForm1.FormCreate(Sender: TObject);

var

  M: TMethod;

begin

  M.Code := @MyMouseDown;

  M.Data := nil;

  Self.OnMouseDown := TMouseEvent(M);

end;

上面的代碼:

Self     -->  EAX

Sender  -->  EDX

Button  -->  CL    // TMouseButton枚舉類型其實內(nèi)部作Byte

其它三個放堆棧。

如果把Self: TObject去掉,則MyMouseDown認(rèn)為只有最后兩個參數(shù)放在堆棧,返回時POP EIP得到的是錯誤的指針,你將看到一個非法訪問內(nèi)存錯。

2)、M.Data := nil;我指的是語法上、運(yùn)行時可為任何值而沒有錯誤。

M.Data保存了方法所對應(yīng)的對象指針,用于在調(diào)用該方法時提供Self這個隱藏參數(shù),在普通過程中其實是沒有用的。難道你還想在過程中用Self來訪問一個按鈕或窗體嗎?M.Data并不是SenderSender是由方法(這里是過程)調(diào)用方指定的。例如VCL源碼:

procedure TControl.DblClick;

begin

  if Assigned(FOnDblClick) then FOnDblClick(Self);

end;

你修改M.Data來作為Sender其實是沒用的。

 

3)、定義M: TMethod只需要賦值時做一次強(qiáng)制類型轉(zhuǎn)換。Delphi的類型強(qiáng)制轉(zhuǎn)換要求類型相容或長度一致,TMethod與對象方法指針是相容的(8字節(jié)),事實上VCL在調(diào)用事件時,也是按TMethod的結(jié)構(gòu)來調(diào)用的,而TNofityEvent等類型在本質(zhì)上與TMethod結(jié)構(gòu)相同,但在Delphi這種強(qiáng)類型語言中使用它們可以避免很多錯誤。

 

 

示例:方法指針指向過程/函數(shù)

代碼:

procedure TestMethod(const pSelf: Pointer; AData: Integer);

begin

    ShowMessage(IntToStr(AData));

end;

 

procedure TForm1.Button2Click(Sender: TObject);

type

    TMyMethod = procedure(AData: Integer) of object;

var

    AMyMethod: TMyMethod;

begin

    with TMethod(AMyMethod) do

    begin

        Code := @TestMethod;

        Data := nil;

    end;

    //---

    AMyMethod(1);

end;

 

示例:方法指針指向過程/函數(shù)

代碼:

type

    TMyObject = class(TObject)

        procedure ExecMethod(const AMsg: string);

    end;

 

procedure TMyObject.ExecMethod(const AMsg: string);

begin

    ShowMessage(AMsg);

end;

 

procedure TestMethod(pSelf: TObject; const AMsg: string);

begin

    ShowMessage(AMsg);

    if pSelf is TMyObject then

        TMyObject(pSelf).ExecMethod(AMsg);

end;

procedure TForm1.Button2Click(Sender: TObject);

var

    AMyObject: TMyObject;

begin

    AMyObject := TMyObject.Create;

    try

        TestMethod(AMyObject,'123');

    finally

        AMyObject.Free;

    end;

end;

代碼:

type

    TFakeEvent = procedure(const AMsg: string) of object;

 

    TMyObject = class(TObject)

    private

        FTestEvent: TFakeEvent;

    public

        constructor Create;

        //---

        procedure ExecTestEvent(const AMsg: string);

        procedure ExecMethod(const AMsg: string);

    end;

 

procedure TestMethod(pSelf: TObject; const AMsg: string);

begin

    ShowMessage(AMsg);

    if pSelf is TMyObject then

        TMyObject(pSelf).ExecMethod(AMsg);

end;

 

constructor TMyObject.Create;

begin

    with TMethod(FTestEvent) do

    begin

        Code := @TestMethod;

        Data := self;

    end;

end;

 

procedure TMyObject.ExecTestEvent(const AMsg: string);

begin

    if Assigned(FTestEvent) then

        FTestEvent(AMsg);

end;

 

procedure TMyObject.ExecMethod(const AMsg: string);

begin

    ShowMessage(AMsg);

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    AMyObject: TMyObject;

begin

    AMyObject := TMyObject.Create;

    try

        AMyObject.ExecTestEvent('123');

    finally

        AMyObject.Free;

    end;

end;

 

示例:方法指針調(diào)用過程/函數(shù)

代碼:

type

    TFakeEvent = procedure(const AMsg: string) of object;

 

    TMyObject = class(TObject)

    private

        FTestEvent: TFakeEvent;

        procedure TestEvent(const AMsg: string);

    public

        constructor Create;

        //---

        procedure ExecTestEvent(const AMsg: string);

    end;

 

procedure TestMethod(const AMsg: string);

begin

    ShowMessage(AMsg);

end;

 

constructor TMyObject.Create;

begin

    FTestEvent := self.TestEvent;

end;

 

procedure TMyObject.ExecTestEvent(const AMsg: string);

begin

    if Assigned(FTestEvent) then

        FTestEvent(AMsg);

end;

 

procedure TMyObject.TestEvent(const AMsg: string);

begin

    TestMethod(AMsg);

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

    AMyObject: TMyObject;

begin

    AMyObject := TMyObject.Create;

    try

        AMyObject.ExecTestEvent('123');

    finally

        AMyObject.Free;

    end;

end;

 

*對象指針(代碼)

示例:方法指針的結(jié)構(gòu)

說明:DELPHI中的對象是是一個32位的指針

代碼:

procedure TForm1.Button2Click(Sender: TObject);

var

    AObject: TObject;

begin

    AObject := TObject.Create;

    try

        Self.Memo1.Lines.Add('對象指針大小:' + IntToStr(SizeOf(AObject)));

    finally

        AObject.Free;

    end;

end;

 

 

示例:對象指針的地址

代碼:

procedure TForm1.Button2Click(Sender: TObject);

var

    pEdit: ^TEdit;

begin

    pEdit := @Edit1;

    //---

    with Self.Memo1.Lines do

    begin

        Add('對象內(nèi)容:' + IntToStr(Integer(Edit1)));

        Add('對象指針的內(nèi)容:' + IntToStr(Integer(pEdit)));

        Add('對象指針指向的內(nèi)容:' + IntToStr(Integer(pEdit^)));

    end;

    //---

    pEdit^.Text := '123';

    pEdit.Text := '123'

end;

說明:

在這里還有一個有趣的現(xiàn)象,我們不但可以用“Edit1^.Text”還可以用“Edit1.Text”方式訪問對象的屬性和方法,其結(jié)果是一樣的,所以在Delphi中只要是對象都是按指針實現(xiàn)的。

 

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多