سؤال

أرغب في الإعلان عن سجل في دلفي يحتوي على نفس التصميم الموجود في لغة C.

للمهتمين :يعد هذا السجل جزءًا من اتحاد في سجل LDT_ENTRY لنظام التشغيل Windows.(أحتاج إلى استخدام هذا السجل في دلفي لأنني أعمل على محاكي Xbox في دلفي - راجع مشروع Dxbx على sourceforge).

على أية حال، يتم تعريف السجل المعني على النحو التالي:

    struct
    {
        DWORD   BaseMid : 8;
        DWORD   Type : 5;
        DWORD   Dpl : 2;
        DWORD   Pres : 1;
        DWORD   LimitHi : 4;
        DWORD   Sys : 1;
        DWORD   Reserved_0 : 1;
        DWORD   Default_Big : 1;
        DWORD   Granularity : 1;
        DWORD   BaseHi : 8;
    }
    Bits;

بقدر ما أعرف، لا توجد حقول بت ممكنة في دلفي.لقد حاولت هذا:

 Bits = record
      BaseMid: Byte; // 8 bits
      _Type: 0..31; // 5 bits
      Dpl: 0..3; // 2 bits
      Pres: Boolean; // 1 bit
      LimitHi: 0..15; // 4 bits
      Sys: Boolean; // 1 bit
      Reserved_0: Boolean; // 1 bit
      Default_Big: Boolean; // 1 bit
      Granularity: Boolean; // 1 bit
      BaseHi: Byte; // 8 bits
  end;

لكن للأسف:ويصبح حجمه 10 بايت، بدلاً من 4 بايت المتوقعة.أود أن أعرف كيف يجب أن أعلن عن السجل، حتى أحصل على سجل بنفس التصميم، ونفس الحجم، ونفس الأعضاء.ويفضل أن يكون ذلك بدون كميات كبيرة من getter/setters.

تيا.

هل كانت مفيدة؟

المحلول

شكرا لكم جميعا!

بناءً على هذه المعلومات، قمت بتقليص ذلك إلى:

RBits = record
public
  BaseMid: BYTE;
private
  Flags: WORD;
  function GetBits(const aIndex: Integer): Integer;
  procedure SetBits(const aIndex: Integer; const aValue: Integer);
public
  BaseHi: BYTE;
  property _Type: Integer index $0005 read GetBits write SetBits; // 5 bits at offset 0
  property Dpl: Integer index $0502 read GetBits write SetBits; // 2 bits at offset 5
  property Pres: Integer index $0701 read GetBits write SetBits; // 1 bit at offset 7
  property LimitHi: Integer index $0804 read GetBits write SetBits; // 4 bits at offset 8
  property Sys: Integer index $0C01 read GetBits write SetBits; // 1 bit at offset 12
  property Reserved_0: Integer index $0D01 read GetBits write SetBits; // 1 bit at offset 13
  property Default_Big: Integer index $0E01 read GetBits write SetBits; // 1 bit at offset 14
  property Granularity: Integer index $0F01 read GetBits write SetBits; // 1 bit at offset 15
end;

يتم ترميز الفهرس على النحو التالي: (BitOffset shl 8) + NrBits.حيث 1<=NrBits<=32 و0<=BitOffset<=31

الآن يمكنني الحصول على هذه البتات وضبطها كما يلي:

{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
function RBits.GetBits(const aIndex: Integer): Integer;
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);

  Result := (Flags shr Offset) and Mask;
end;

procedure RBits.SetBits(const aIndex: Integer; const aValue: Integer);
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);
  Assert(aValue <= Mask);

  Flags := (Flags and (not (Mask shl Offset))) or (aValue shl Offset);
end;

أنيق جدا ، ألا تعتقد ذلك؟!؟!

ملاحظة:قام رودي فيلثويس الآن بتضمين نسخة منقحة من هذا في كتابه الممتاز مقالة "مزالق التحول"..

نصائح أخرى

ركن رودي دلفي هو أفضل مورد أعرفه فيما يتعلق بإمكانية التشغيل البيني لـ Delphi وC/C++.له معوقات التحويل يجب قراءته إلى حد كبير عند استخدام واجهات برمجة تطبيقات C/C++ في دلفي.الفصل الذي ستهتم به أكثر هو السجلات والمحاذاة -> حقول البت, لكن أنصحك بقراءة الموضوع كاملاً من الأعلى إلى الأسفل، مرتين.المقالات الأخرى تستحق بالتأكيد استثمار الوقت أيضًا.

حسنًا، معالجة البت الخاصة بي كانت صدئة بعض الشيء، لذا كان بإمكاني عكس البايتات.لكن الكود أدناه يعطي فكرة عامة:

type
  TBits = record
  private
    FBaseMid     : Byte;
    FTypeDplPres :  Byte;
    FLimitHiSysEa: Byte;
    FBaseHi      : Byte;

    function GetType: Byte;
    procedure SetType(const AType: Byte);
    function GetDpl: Byte;
    procedure SetDbl(const ADpl: Byte);
    function GetBit1(const AIndex: Integer): Boolean;
    procedure SetBit1(const AIndex: Integer; const AValue: Boolean);
    function GetLimitHi: Byte;
    procedure SetLimitHi(const AValue: Byte);
    function GetBit2(const AIndex: Integer): Boolean;
    procedure SetBit2(const AIndex: Integer; const AValue: Boolean);

  public
    property BaseMid: Byte read FBaseMid write FBaseMid;
    property &Type: Byte read GetType write SetType; // 0..31
    property Dpl: Byte read GetDpl write SetDbl; // 0..3
    property Pres: Boolean index 128 read GetBit1 write SetBit1; 
    property LimitHi: Byte read GetLimitHi write SetLimitHi; // 0..15

    property Sys: Boolean index 16 read GetBit2 write SetBit2; 
    property Reserved0: Boolean index 32 read GetBit2 write SetBit2; 
    property DefaultBig: Boolean index 64 read GetBit2 write SetBit2; 
    property Granularity: Boolean index 128 read GetBit2 write SetBit2; 
    property BaseHi: Byte read FBaseHi write FBaseHi;
  end;

  function TBits.GetType: Byte;
  begin
    Result := (FTypeDplPres shr 3) and $1F;
  end;

  procedure TBits.SetType(const AType: Byte);
  begin
    FTypeDplPres := (FTypeDplPres and $07) + ((AType and $1F) shr 3);
  end;

  function TBits.GetDpl: Byte;
  begin
    Result := (FTypeDplPres and $06) shr 1;
  end;

  procedure TBits.SetDbl(const ADpl: Byte);
  begin
    FTypeDblPres := (FTypeDblPres and $F9) + ((ADpl and $3) shl 1);
  end;

  function TBits.GetBit1(const AIndex: Integer): Boolean;
  begin
    Result := FTypeDplPres and AIndex = AIndex;
  end;

  procedure TBits.SetBit1(const AIndex: Integer; const AValue: Boolean);
  begin
    if AValue then
      FTypeDblPres := FTypeDblPres or AIndex
    else
      FTypeDblPres := FTypeDblPres and not AIndex;
  end;

  function TBits.GetLimitHi: Byte;
  begin
    Result := (FLimitHiSysEa shr 4) and $0F;
  end;

  procedure TBits.SetLimitHi(const AValue: Byte);
  begin
    FLimitHiSysEa := (FLimitHiSysEa and $0F) + ((AValue and $0F) shr 4);
  end;

  function TBits.GetBit2(const AIndex: Integer): Boolean;
  begin
    Result := FLimitHiSysEa and AIndex = AIndex;
  end;

  procedure TBits.SetBit2(const AIndex: Integer; const AValue: Boolean);
  begin
    if AValue then
      FLimitHiSysEa := FLimitHiSysEa or AIndex
    else
      FLimitHiSysEa := FLimitHiSysEa and not AIndex;
  end;

حسنًا، أنت في الأساس بحاجة إلى النزول إلى الأمور القذرة من خلال التلاعب بالبتات.

لماذا، على وجه التحديد، هل تحتاج إلى الاحتفاظ بهذا الهيكل؟

إذا كنت تحتاج فقط إلى التحدث إلى برنامج قديم يتحدث بهذه اللهجة (TCP/IP أو ما شابه)، أو يخزن البيانات بهذه الطريقة (الملفات، وما إلى ذلك)، فسوف أقوم بتعيين بنية دلفي عادية إلى إصدار بت متناسق.بمعنى آخر، سأستخدم بنية دلفي منظمة بشكل طبيعي في الذاكرة، وأكتب التعليمات البرمجية لكتابة وقراءة تلك البنية بطريقة متوافقة.

إذا كنت بحاجة إلى حفظ الذاكرة، فسأقوم بإنشاء حروف ومحددات تتعامل مع أجزاء من الأعداد الصحيحة الداخلية أو ما شابه ذلك.سيكون لهذا تأثير على الأداء، ولكن ليس أكثر بكثير مما سيكون عليه برنامج C الأصلي، والفرق الوحيد هو أن معالجة البت ستتم إضافتها بواسطة سحر المترجم في إصدار C، بينما سيتعين عليك كتابتها بنفسك.

إذا لم يكن لديك العديد من السجلات في الذاكرة، ولا تحتاج إلى التحدث إلى برنامج آخر، فسأستخدم بنية دلفي الطبيعية.ستكون المقايضة للحصول على أداء أعلى هي استخدام المزيد من الذاكرة.

ولكن كل هذا يتوقف على المعايير الخاصة بك.

على أية حال، لن تتمكن من إقناع مترجم دلفي بالقيام بنفس المهمة التي يقوم بها مترجم لغة C.

السجل المعبأ، الذي اقترحه شخص آخر هنا، لا يفعل ذلك، ولم يكن من المفترض أن يفعل ذلك على الإطلاق.سيؤدي ذلك فقط إلى إزالة حشوة المحاذاة لوضع الأعداد الصحيحة على حدود 32 بت وما شابه ذلك، ولكنها لن تقوم بتعبئة حقول متعددة في بايت واحد.

لاحظ أن الطريقة الشائعة للقيام بذلك هي من خلال مجموعات دلفي، والتي يتم تنفيذها داخليًا باستخدام حقول البت.مرة أخرى، سيكون لديك رمز مختلف عن المتغير C.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top