跳轉到內容

Ada 程式設計/屬性/'位序

來自華夏公益教科書,開放的書籍,開放的世界

Ada. Time-tested, safe and secure.
Ada. 經久考驗,安全可靠。

R'Bit_Order 是一個用於指定表示屬性,用於指定位編號記錄表示子句(對於記錄型別)。 位排序是System.Bit_Order 型別,可以是System.Bit_Order,可以是

假設位序列被解釋為整數值。常量System.Default_Bit_Order表示平臺的本機位編號。

值得注意的是,位排序隻影響記錄表示子句的位編號,而不是其(多位元組)欄位的位元組序[1]

儲存單元按其地址排序。讓我們看看佔用多個位元組的整數(假設位元組為 8 位)。

  • 在大端(BE)機器上,整數的最高有效位元組儲存在最低地址。
  • 在小端(LE)機器上,其最低有效位元組儲存在最低地址。

因此,為了能夠跨位元組邊界連續計數位,Ada 按照大端的最高有效位(MSB)0 到最低有效位(LSB)的順序計數字節內的位,而在小端上則反過來。[2]

這就是它的樣子(在大端上方便地從左到右寫,在小端上從右到左寫)

BE Byte    0 1 2 …  (counting bytes, i.e. addresses, left to right; higher addresses to the right)
LE Byte  … 2 1 0    (counting bytes right to left; higher addresses to the left)

         MSB         LSB
BE  Bit  0 1 2 3 4 5 6 7  (counting bits within a byte left to right)
LE  Bit  7 6 5 4 3 2 1 0  (counting bits right to left)

我們習慣了從左到右寫,但是對於小端,正如你所見,從右到左寫地址很方便。因此,在大端上,位元組和位的序列按以下方式計數

Byte 00                      01                      02
Bit  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 …
                             00 01 02 03 04 05 06 07 08 09 10 11 12 13 …  (we can equally begin to count at byte 01)

為了能夠從小端連續寫入和計數,我們必須像阿拉伯語或希伯來語指令碼那樣從右到左寫(MSB 始終在左側;這似乎是所有現代指令碼中的一個全球性特徵)

Byte                  02                      01                      00
Bit  … 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
     … 13 12 11 10 09 08 07 06 05 04 03 02 01 00

在以下記錄表示中(當然,只有指定 X 的兩行中的一行可能存在)

for T use record
  X at 0 range 13 .. 17;  -- these two lines…
  X at 1 range  5 ..  9;  -- … are equivalent
end record;

元件 X 佔用粗體所示的位。位元組編號(0 或者 1)稱為 Position,邊界稱為 First_Bit(13 或者 5)和 Last_Bit(17 或者 9);有相應的屬性'Position'First_Bit'Last_Bit。(元件 X 的含義在這裡無關緊要,只有它的大小相關)。正如你所見,X 是一個跨邊界的元件。因此,將 'Bit_Order 屬性應用於具有跨邊界元件的記錄的結果取決於 Ada 的版本。

Ada 95 僅在應用於儲存單元內或專案完全填充儲存單元序列時,才定義屬性在非本機位序上的結果。透過將屬性應用於記錄,我們強制編譯器以指示的方式計數位,獨立於機器體系結構。

type Rec is record
  A at 0 range 0 .. 5;
  B at 0 range 6 .. 7;
end record;
for Rec'Bit_Order use High_Order_First;

元件 B 佔用粗體所示的位

Byte 0
Bit  0 1 2 3 4 5 6 7

此記錄的佈局在 BE 和 LE 機器上將相同,即表示是與位元組序無關的。

我們也可以這樣定義此記錄,並獲得相同的與位元組序無關的佈局

type Rec is record
  A at 0 range 2 .. 7;
  B at 0 range 0 .. 1;
end record;
for Rec'Bit_Order use Low_Order_First;
Byte               0
Bit  7 6 5 4 3 2 1 0

但是,以下記錄,其中 B 是跨邊界的,僅在 BE 機器上有效,即在本機位序中。

type Rec is record
  A at 0 range 0 .. 5;
  B at 0 range 6 .. 9;
end record;
for Rec'Bit_Order use High_Order_First;
Byte  0                       1
Bit   0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
#    00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15

在 LE 機器上編譯時,編譯器會抱怨 B 佔用非連續儲存並拒絕程式碼,因為位元組序不受屬性影響(記住,下一個帶有位號 8 到 15 的位元組必須放在左側)

Byte                         1                       0
Bit    00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07
#      08 09 10 11 12 13 14 15 00 01 02 03 04 05 06 07

作為專案填充完整儲存單元範圍的記錄示例,請參見

type Rec is record
  A at 0 range 0 ..  7;
  B at 1 range 0 .. 15;
end record;
for Rec'Bit_Order use High_Order_First;

這在兩種體系結構上都有效,導致以下佈局

BE    0                       1                       2
Bit  00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
LE                         2                       1                       0
Bit  08 09 10 11 12 13 14 15 00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07

其中 A 填充普通字型中的位,B 填充粗體字型中的位。正如你所見,在 LE 上以這種奇怪的方式計數位並不重要,因為元件 B 完全填充了它的兩個位元組。

對於本機位序,沒有變化。記錄表示規範從 Ada 83 開始就有效。只有 'Bit_Order 屬性在 Ada 95 中引入,並具有上述限制。

為了改善非本機位序的情況,Ada 2005 引入了機器標量的概念。機器標量是一個概念上的基於硬體的無符號整數,在其中計數位並按要求定位元件。

現在我們需要一個更詳細的記錄示例(相應的屬性 'Position、'First_ 和 'Last_Bit(在本機位序中)返回的值作為註釋給出;注意,返回的位號始終從元件開始的位元組開始計數)

for T use record
  I at 0 range  0 .. 15;  -- at 0 range 0 .. 15
  A at 2 range  0 .. 12;  -- at 2 range 0 .. 12
  B at 2 range 13 .. 17;  -- at 3 range 5 ..  9
  C at 2 range 18 .. 23;  -- at 4 range 2 ..  7
  D at 5 range  0 ..  7;  -- at 5 range 0 ..  7
end record;

假設 I 是一個 16 位(有符號或無符號)整數,其他元件是某種未指定的型別,具有給定的尺寸要求。整個記錄的大小為 48。這就是它在 BE 和 LE 機器上的樣子

Byte 0       1       2       3       4       5
  BE 012345678901234501234567890123456789012301234567
     IIIIIIIIIIIIIIIIAAAAAAAAAAAAABBBBBCCCCCCDDDDDDDD

     DDDDDDDDCCCCCCBBBBBAAAAAAAAAAAAAIIIIIIIIIIIIIIII
  LE 765432103210987654321098765432105432109876543210
Byte        5       4       3       2       1       0

在下面,我們將展示為了實現表示的與位元組序無關性,我們能走多遠。結果將是,當正確使用新的 Ada 2005 功能時,我們只需要在從一種體系結構傳輸到另一種體系結構後交換位元組。

對於以下內容,我們將假設我們是在一個大端機器上,因此我們將相應的屬性附加到表示中

for T use record
  I at 0 range  0 .. 15;
  A at 2 range  0 .. 12;
  B at 2 range 13 .. 17;
  C at 2 range 18 .. 23;
  D at 5 range  0 ..  7;
end record;
for T'Bit_Order use High_Order_First;

當這在小端機器上編譯時,所有具有相同 Position 的元件將被組合在一起並放入匹配的機器標量中。機器標量位於給定的 Position 處,但在內部,位從相反方向計數。

讓我們以第一個元件 I 為例。它使用 16 位,因此可以使用 16 位機器標量。它位於位元組 0 處,但在內部,編譯器將從高位端計數。這就是它的樣子(NNBO - 非本機位序)

                                     IIIIIIIIIIIIIIII
NNBO                                 0123456789012345
Byte        5       4       3       2       1       0

現在到下一個 Position 2。相應的元件 A、B、C 總共使用三個位元組,因此需要一個 32 位機器標量。它位於位元組 2 處,在內部計數將再次從相反方向開始。這就是它的樣子

                                     IIIIIIIIIIIIIIII
NNBO 012345678901234567890123456789010123456789012345
Byte        5       4       3       2       1       0

位計數從位元組 5 的最高有效位開始,一直持續到位元組 2 的最低有效位。元件 A、B、C 在此標量內部根據各自的範圍進行定位。因此,我們得到了以下佈局

     AAAAAAAAAAAAABBBBBCCCCCC        IIIIIIIIIIIIIIII
NNBO 012345678901234567890123456789010123456789012345
Byte        5       4       3       2       1       0

我們立即看到與元件 D 的衝突,它的範圍已被 A 佔用,編譯器當然會報錯並拒絕程式碼。解決方案很簡單:只需將 D 的位置從 5 更改為 (在 BE 上) 等效行,如下所示

for T use record
  I at 0 range  0 .. 15;
  A at 2 range  0 .. 12;
  B at 2 range 13 .. 17;
  C at 2 range 18 .. 23;
--D at 5 range  0 ..  7;
  D at 2 range 24 .. 31;
end record;
for T'Bit_Order use High_Order_First;

並且,鼓聲響起,在 LE 上,我們現在有(為了比較,還給出了本機佈局)

     AAAAAAAAAAAAABBBBBCCCCCCDDDDDDDDIIIIIIIIIIIIIIII
NNBO 012345678901234567890123456789010123456789012345
Byte        5       4       3       2       1       0

     IIIIIIIIIIIIIIIIAAAAAAAAAAAAABBBBBCCCCCCDDDDDDDD
  BE 012345678901234501234567890123456789012345678901
Byte 0       1       2       3       4       5

在非本機位順序中,由相應屬性“Position”、“First_”和“Last_Bit”返回的值與記錄規範中給出的值完全一致。

作為一項額外服務,GNAT 的編譯輸出將為您提供在機器標量中以本機位順序計算的值

       range
"I"   0 .. 15
"A"  19 .. 31
"B"  14 .. 18
"C"   8 .. 13
"D"   0 ..  7

     AAAAAAAAAAAAABBBBBCCCCCCDDDDDDDDIIIIIIIIIIIIIIII
NNBO 012345678901234567890123456789010123456789012345
  LE 109876543210987654321098765432105432109876543210
Byte        5       4       3       2       1       0

這是我們在當前 Ada 標準下所能達到的極限。

資料傳輸

[編輯 | 編輯原始碼]

讓我們將此記錄的值從本機大端機傳輸到小端機。為了演示目的,跨邊界專案的最高位部分顯示為大寫,最低位部分顯示為小寫。

     IIIIIIIIiiiiiiiiAAAAAAAAaaaaaBBBbbCCCCCCDDDDDDDD
  BE 012345678901234501234567890123456789012345678901
Byte 0       1       2       3       4       5

位元組將按給定順序傳輸。由於位順序屬性不會在傳輸後重新排序位元組,因此在目標機器上,我們將以亂序接收資料

     DDDDDDDDbbCCCCCCaaaaaBBBAAAAAAAAiiiiiiiiIIIIIIII
NNBO 012345678901234567890123456789010123456789012345
Byte        5       4       3       2       1       0

我們所要做的就是交換位元組 0↔1、2↔5、3↔4,以達到預期的結果

     AAAAAAAAaaaaaBBBbbCCCCCCDDDDDDDDIIIIIIIIiiiiiiii
NNBO 012345678901234567890123456789010123456789012345
Byte        5       4       3       2       1       0

以下兩組表示子句在任何機器/編譯器(Ada 2005 及更高版本)中指定了相同的暫存器佈局

type Device_Register is
    record
       Ready : Status_Flag;
       Error : Error_Flag;
       Data  : Unsigned_16;
    end record;

for  Device_Register use
    record
       Ready at 0 range  0 .. 0;
       Error at 0 range  1 .. 1;
       -- Reserved bits
       Data  at 0 range 16 .. 31;
    end record;
for Device_Register'Size      use 32;
for Device_Register'Bit_Order use System.Low_Order_First;

pragma Atomic (Device_Register);

如果位順序修改,則必須反轉記錄表示子句中所有元素的位編號

type Device_Register is
    record
       Ready : Status_Flag;
       Error : Error_Flag;
       Data  : Unsigned_16;
    end record;

for  Device_Register use
    record
       Ready at 0 range 31 .. 31;   -- Bit numbering has changed
       Error at 0 range 30 .. 30;   -- Bit numbering has changed
       -- Reserved bits
       Data  at 0 range  0 .. 15;   -- Bit numbering has changed (but byte order is not affected)
    end record;
for Device_Register'Size      use 32;
for Device_Register'Bit_Order use System.High_Order_First; -- Reverse bit order

pragma Atomic (Device_Register);

兩者可以在同一臺機器上互換使用。但請注意,在兩臺具有不同位元組序的機器中資料欄位將具有本機位元組序,而與表示子句中指定的位順序無關。

不正確的用法

[編輯 | 編輯原始碼]

'Bit_Order 屬性並非旨在將資料在大小端機器之間轉換(它影響位編號,而不是位元組順序)。當指定非本機位順序時,編譯器不會生成程式碼來重新排序多位元組欄位。[3][4][5]

參考資料

[編輯 | 編輯原始碼]
  1. 請注意,當 ARM 在 'Bit_Order 屬性的定義中談論“大端”和“小端”時,它實際上指的是位位元組序,而不是位元組位元組序。(如今,術語位元組序通常保留用於討論位元組順序,儘管它也可以用於位編號。)因此,在這種情況下,當 ARM 說“小端”時,它指的是“LSB 0”,而當它說“大端”時,它與“MSB 0”相同:

    «High_Order_First(在口語中稱為“大端”)表示儲存元素的第一個位(位 0)是最重要的位(將表示元件的位序列解釋為無符號整數)。Low_Order_First(在口語中稱為“小端”)表示相反:第一個位是最不重要的位。» [LRM, §13.5.3(2)]

  2. ISO/IEC 8652:1987. "13.4 Record Representation Clauses". Ada 83 Reference Manual. The range defines the bit positions of the storage place, relative to the storage unit. The first storage unit of a record is numbered zero. The first bit of a storage unit is numbered zero. The ordering of bits in a storage unit is machine_dependent and may extend to adjacent storage units. {{cite book}}: Unknown parameter |chapterurl= ignored (|chapter-url= suggested) (help)
  3. AI95-00133-01 (1996-05-07). "Controlling bit ordering". Class: binding interpretation. Ada Rapporteur Group. Bit_Order clauses are concerned with the numbering of bits and not concerned with data flipping interoperability.
  4. ISO/IEC 8652:2007. "13.5.3 Bit Ordering (9/2)". Ada 2005 Reference Manual. Retrieved 2008-06-02. Bit_Order clauses make it possible to write record_representation_clauses that can be ported between machines having different bit ordering. They do not guarantee transparent exchange of data between such machines. {{cite book}}: Unknown parameter |chapterurl= ignored (|chapter-url= suggested) (help)
  5. Thomas Quinot (2013). "Gem #140: Bridging the Endianness Gap". AdaCore. Retrieved 2013-01-31. the order in which the bytes that constitute machine scalars are written to memory is not changed by the Bit_Order attribute -- only the indices of bits within machine scalars are changed. {{cite web}}: Unknown parameter |month= ignored (help)


據作者所知,記錄表示子句是 Ada 語言的獨特功能,因此不需要在其他程式語言中使用類似於屬性 'Bit_Order 的功能。在其他程式語言中,通常的做法是顯式使用掩碼和位運算,因此必須始終使用本機位編號。

也可以看看

[編輯 | 編輯原始碼]

華夏公益教科書

[編輯 | 編輯原始碼]

Ada 參考手冊

[編輯 | 編輯原始碼]

Ada 理論

[編輯 | 編輯原始碼]

參考文獻和註釋

[編輯 | 編輯原始碼]

進一步閱讀

[編輯 | 編輯原始碼]
華夏公益教科書