簡體   English   中英

位域和訪問說明符

[英]Bit fields and access specifiers

是否允許在 c++ 中為位域指定不同的訪問說明符? 前任。:

struct S1 {
   unsigned f1    : 2;
   unsigned f2    : 2;
private: // 1
   unsigned f3    : 2;
public: // 2
   unsigned f4    : 2;
};

所以問題是它是否允許在第 1 行和第 2 行中指定訪問說明符?

簡短的回答

是的。

長答案

另一方面,打破嚴格的別名規則則不然。 這是一個常見的問題,人們只是假設關於這個和其他 C++ 語言特性的事情,然后盲目地在沒有問題的地方尋找問題。

您在評論中提到的這個“奇怪”錯誤聞起來很像您試圖將 std::byte 重新解釋為您的結構,盡管“顯然”可能是未定義的行為。 這里唯一奇怪的是在處理低級數據存儲時的大量懶惰,這在寫這個問題實際上付出了多少努力時也很明顯。

好吧,無論如何,您為什么要嘗試將一個字節重新解釋為您的位域結構,即使這不是您正在做的事情? 因為它更快? 比什么,您的程序甚至從未正確編譯,因為您的編譯器假定您不會這樣做? 未定義的行為是未定義的,但后果顯然是。

您必須記住,位域不可移植,主要是為了方便,而不是必需品。 如果你想要速度,你每次都會放棄便利,例如,假設你正在做的事情會更快而不分析你的代碼的便利。 如果您分析您的代碼並確定您可以通過網絡發送或獨立寫入磁盤平台的原始字節之間的所有這些“復雜”轉換確實是一個瓶頸,您將從位域退回到手動排列字節,這就是我們程序員生活的現實。

同時,您問題中的此類構造完全沒有價值,因此即使不可能擁有私有位字段,也不會成為問題,因為原始數據(不是 POD。)應該像原始數據一樣對待數據,如果您想要一些特定的抽象行為,您可以將這些原始數據作為私有成員放入真正的 class 中,所有字段都對封閉的 class 公開並從那里工作。

但我離題了,這就是每個人在閱讀這個類似咆哮的解釋時都在痛苦地渴望的東西——一個清晰、干凈、明顯、正確、獨立於平台和理智的真實例子:

#include <cstddef>
#include <iostream>

struct packed_struct {
    unsigned a: 2;
    unsigned b: 2;
    unsigned c: 1;
    unsigned d: 2;
    unsigned e: 1;

    packed_struct() noexcept
    : a{},
      b{},
      c{},
      d{},
      e{}
    {}

    // no, this struct is NOT supposed to be a POD, the POD is right here, std::byte, the
    // plainest and oldest data you will ever see
    explicit packed_struct(std::byte raw) noexcept
    // you only need to get this right once
    // https://en.cppreference.com/w/cpp/language/operator_precedence
    : a(static_cast<unsigned>(raw) >> 0 & 0b11),
      d(static_cast<unsigned>(raw) >> 2 & 0b11),
      b(static_cast<unsigned>(raw) >> 4 & 0b11),
      e(static_cast<unsigned>(raw) >> 6 &  0b1),
      c(static_cast<unsigned>(raw) >> 7 &  0b1)
      // what is this arbitrary order you ask? well... not all data we get is
      // our own, so it is not always ordered how we might want it to be;
      // in our project we have this stupid rule to order same size fields
      // alphabetically, and while noone likes it, noone wants to challenge
      // whoever made it up, so this will have to do
      // think this is unrealistic? Think again
    {}

    explicit operator std::byte() const noexcept
    {
        std::byte raw{};
        raw |= static_cast<std::byte>(a << 0);
        raw |= static_cast<std::byte>(d << 2);
        raw |= static_cast<std::byte>(b << 4);
        raw |= static_cast<std::byte>(e << 6);
        raw |= static_cast<std::byte>(c << 7);
        return raw;
    }
};

// well, where's the benefit of this if we did it all manually? the answer is
// we did only what we always have to, while compiler did a lot of free and a
// lot more annoying work for us
int main() {
    // there is a clear line between human understandable data
    packed_struct s;
    s.a = 1;
    s.b = 3;
    s.c = 0;
    s.d = 2;
    s.e = 1;

    // and an unintelligible block of bits that noone needs to know the layout of
    // but we do know the layout, we wrote the conversion functions, didn't we?
    // no, I in fact do not know which bit is "3rd" or "7th" or even "29th", 
    // and guess what, I don't care either
    std::byte b = static_cast<std::byte>(s);

    // but can still be effortlessly stored in a file and even moved across
    // systems, ignoring any details including processor architecture...

    // and yet, they can all be converted back and forth effortlessly
    packed_struct r(~b); // you can even mess up the bits in a POD

    // and still get exactly what you expect, no assumptions, ever
    std::cout << "r.a = " << r.a << '\n'  // 2
              << "r.b = " << r.b << '\n'  // 0
              << "r.c = " << r.c << '\n'  // 1
              << "r.d = " << r.d << '\n'  // 1
              << "r.e = " << r.e << '\n'; // 0
}

std::byte更大的類型也可以這樣做,如果您了解這背后的基本思想,那么您將毫無問題地弄清楚如何使用完全相同的邏輯來編碼和解碼unsigned short , long int ...只要有支持它的編譯器,即使在每個字節訪問都具有隨機字節順序的系統上也能正常工作。

既然我已經寫了這么多,我不妨舉一個更大類型的例子:

unsigned short b{};
std::array<std::byte, 2> bytes;
// <read 2 bytes from somewhere like using fread and a.data()>

這是唯一重要的部分——哪個字節在哪里? 不,這與你的 CPU 架構完全無關,你不應該把這些東西混為一談。 我可以先寫后寫,也可以先寫后寫,不管什么機器運行它都一樣,只要你保持一致,我不需要告訴你保持一致。

b |= static_cast<unsigned short>(bytes[0] << 8);
b |= static_cast<unsigned short>(bytes[1] << 0);
packed_struct_2bytes s2(b); // TODO: implement this type

這里要指出的是,您定義的格式是針對程序員的,而不是針對機器的。 是的,它是“從第 3 個字節到第 6 位的 2 位”,但是您必須了解這實際上意味着什么。 Your CPU could count bytes backwards, or even go back and forth between the ends by treating 4 byte integer type as {1, 3, 4, 2} (not to be confused with C array syntax), where a[2] == 2 . 您不需要知道這一點,就像您在使用位域功能時不需要知道位是如何打包的一樣。 我們已經編寫了編程語言,因為處理真實機器很困難。 不利的一面是,“了解”關於機器的事情在極端情況下對你沒有幫助,但這對你來說並不重要,而且當它需要時,你將成為向所有人解釋事情的人,而不是提出問題。

一旦你了解了這些瑣碎的事情,你所有的“奇怪的錯誤”都會永久消失。

不出所料,如果你真的需要這個,你可以在這個結構中將任何字段設為私有,它會工作得很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM