簡體   English   中英

字節緩沖區應該是有符號還是無符號字符緩沖區?

[英]Should a buffer of bytes be signed or unsigned char buffer?

字節緩沖區應該是有符號字符還是無符號字符,還是只是一個字符緩沖區? C 和 C++ 之間有什么區別?

謝謝。

如果您打算存儲任意二進制數據,則應使用unsigned char 它是 C 標准保證沒有填充位的唯一數據類型。 每個其他數據類型可能在其 object 表示形式中包含填充位(即包含 object 的所有位,而不僅僅是那些確定值的位)。 填充位的 state 未指定,不用於存儲值。 因此,如果您使用char讀取一些二進制數據,則會將內容縮減到 char 的值范圍(通過僅解釋值位),但可能仍然有一些位被忽略但仍然存在並由memcpy讀取。 很像真實結構對象中的填充位。 類型unsigned char保證不包含這些。 5.2.4.2.1/2開始(這里是 C99 TC2,n1124):

如果 char 類型的 object 的值在表達式中使用時被視為帶符號的 integer,則CHAR_MIN的值應與SCHAR_MIN的值相同, CHAR_MAX的值應與SCHAR_MAX的值相同。 否則, CHAR_MIN的值應為 0, UCHAR_MAX的值應與CHAR_MAX的值相同。 UCHAR_MAX值應等於2^CHAR_BIT − 1

從最后一句可以看出,沒有任何空間可供任何填充位使用。 如果您使用char作為緩沖區的類型,您還會遇到溢出問題:將任何值顯式分配給一個8位范圍內的此類元素 - 所以您可能期望這樣的分配是可以的 - 但不在char的范圍,即CHAR_MIN .. CHAR_MAX ,這樣的轉換會溢出並導致實現定義的結果,包括信號的提升。

即使與上述有關的任何問題都可能不會在實際實現中顯示(將是非常差的實現質量),您最好從一開始就使用正確的類型,即unsigned char

但是,對於字符串,選擇的數據類型是char ,字符串和打印函數可以理解它。 出於這些目的使用signed char對我來說似乎是一個錯誤的決定。

有關更多信息,請閱讀this proposal ,其中包含對下一版本 C 標准的修復,該標准最終將要求有signed char也沒有任何填充位。 它已被納入工作文件

字節緩沖區應該是有符號字符還是無符號字符,還是只是一個字符緩沖區? C 和 C++ 之間有什么區別?

語言如何處理它的細微差別。 約定如何處理它的巨大差異。

  • char = ASCII(或 UTF-8,但有符號阻礙)文本數據
  • unsigned char = 字節
  • signed char = 很少使用

還有一些代碼依賴於這種區別。 就在一兩周前,我遇到了一個錯誤,即 JPEG 數據被損壞,因為它被傳遞給我們的 Base64 編碼 function 的char*版本——它“有用地”替換了“字符串”中的所有無效 ZAE3B3DF9970B49BBC9523E608DZ9。 更改為BYTE aka unsigned char即可修復它。

這取決於。

如果緩沖區旨在保存文本,那么將其聲明為char數組並讓平台為您決定默認情況下是有符號還是無符號可能是有意義的。 例如,這將使您在將數據傳入和傳出實現的運行時庫時遇到的麻煩最少。

如果緩沖區旨在保存二進制數據,則取決於您打算如何使用它。 例如,如果二進制數據確實是數據樣本的打包數組,這些數據樣本是有符號的 8 位定點 ADC 測量值,那么有signed char是最好的。

在大多數實際情況下,緩沖區只是一個緩沖區,並且您並不真正關心各個字節的類型,因為您在批量操作中填充了緩沖區,並且您即將將其傳遞給解析器來解釋復雜的數據結構並做一些有用的事情。 在這種情況下,以最簡單的方式聲明它。

如果它實際上是 8 位字節的緩沖區,而不是機器默認語言環境中的字符串,那么我會使用uint8_t 並不是說在很多機器周圍 char 不是字節(或字節不是八位字節),而是說“這是八位字節的緩沖區”而不是“這是一個字符串”通常是有用的文檔。

您應該使用charunsigned char但從不使用signed char 該標准在 3.9/2 中有以下內容

對於 POD 類型 T 的任何 object(基類子對象除外),無論 object 是否持有類型 T 的有效值,構成 ZA8CFDE6331BD59EB2AC96F8911C4B666 的基礎字節(1.7)都可以復制到數組中char。如果 char 或 unsigned char 數組的內容被復制回 object,則 object 隨后應保持其原始值。

最好將其定義為無符號字符。 事實上 Win32 類型 BYTE 被定義為無符號字符。 C 和 C++ 之間沒有區別。

為了獲得最大的可移植性,請始終使用無符號字符。 有幾個例子可以發揮作用。 立即想到在具有不同字節序類型的系統之間共享的序列化數據。 當執行移位或位掩碼時,這些值是另一個值。

int8_t 與 uint8_t 的選擇類似於將 ptr 與 NULL 進行比較。


從功能的角度來看,比較 NULL 與比較 0 相同,因為 NULL 是 0 的#define。

但就個人而言,從編碼風格的角度來看,我選擇將我的指針與 NULL 進行比較,因為 NULL #define 意味着維護代碼的人正在檢查是否存在錯誤指針......

VS

當有人看到與 0 的比較時,這意味着您正在檢查特定值。


由於上述原因,我會使用 uint8_t。

如果您將一個元素提取到一個更廣泛的變量中,那么它當然會被符號擴展或不擴展。

unsigned, since it feels more "raw", less inviting to say "hey, that's just a bunch of small ints ", if I want to emphasize the binary-ness of the data.應該並且應該......我傾向於無符號,因為如果我想強調數據的二進制性,它感覺更“原始”,而不是說“嘿,這只是一堆小ints ”。

我認為我從來沒有使用過顯式的有signed char來表示字節緩沖區。

當然,第三種選擇是盡可能將緩沖區表示為void * 許多常見的 I/O 函數都與void *一起使用,因此有時可以完全封裝決定使用什么 integer 類型,這很好。

幾年前,我遇到了一個 C++ 控制台應用程序的問題,該應用程序為 128 以上的 ASCII 值打印彩色字符,這可以通過從 char 切換到 unsigned char 來解決,但我認為它在保持 char 類型的同時也是可以解決的。

目前,大多數 C/C++ 函數都使用 char,而且我現在對這兩種語言的理解要好得多,所以我在大多數情況下都使用 char。

你真的在乎嗎? 如果你不這樣做,只需使用默認值 (char) 並且不要用無關緊要的事情弄亂你的代碼。 否則,未來的維護者會想知道你為什么使用有符號(或無符號)。 讓他們的生活更簡單。

如果你對編譯器撒謊,它會懲罰你。

如果緩沖區包含剛剛通過的數據,並且您不會以任何方式操作它們,那沒關系。

但是,如果您必須對緩沖區內容進行操作,那么正確的類型聲明將使您的代碼更簡單。 沒有“int val = buf[i] & 0xff;” 廢話。

因此,請考慮數據實際上是什么以及您需要如何使用它。

typedef char byte;

現在您可以使您的數組為byte s。 每個人都清楚您的意思,並且您不會丟失任何功能。

我知道這有點傻,但它使您的代碼按照您的預期 100% 閱讀。

暫無
暫無

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

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