2013-02-08 58 views
7

指針有什麼用處unsigned char?我曾在很多地方看到過,指針被類型轉換爲指向unsinged char的指針爲什麼我們要這樣做?何時使用無符號字符指針

我們收到一個指向int的指針,然後將其轉換爲unsigned char*。但是如果我們嘗試使用cout在該數組中打印元素,它不會打印任何內容。爲什麼?我不明白。我是新來的C++。

編輯下面的代碼示例

int Stash::add(void* element) 
{ 
    if(next >= quantity) 
    // Enough space left? 
     inflate(increment); 

    // Copy element into storage, starting at next empty space: 
    int startBytes = next * size; 
    unsigned char* e = (unsigned char*)element; 
    for(int i = 0; i < size; i++) 
     storage[startBytes + i] = e[i]; 
    next++; 
    return(next - 1); // Index number 
} 
+0

當轉換爲字符指針時,第一個字節可能爲零,這與字符串終止符相同,因此不會打印任何內容。如果你能展示你真正做的事情,比如發佈一些代碼,它將會有所幫助。請製作[SSCCE](http://sscce.org/)並添加到問題中。 –

+0

但我認爲如果第一個字節爲零,實際上我試圖打印所有的四個字節,但它不打印任何東西會丟失信息。 –

+2

你的問題似乎更多關於「爲什麼」而不是「什麼時候」。通常,'unsigned char *'被用作一個字節級的訪問方法,以便進入一個更正式類型的變量或內存地址。它具有許多優點,其中包括免除嚴格的別名規則和標準保證與您輸入的任何地址的一致性。 C++新手不應該讓這個問題變得困難,因爲如果你對C程序有相當的熟悉程度,那麼我認爲這是一個難以理解的挑戰。也許你有一些代碼和背後的想法你有問題嗎? – WhozCraig

回答

5

你實際上是在尋找pointer arithmetic

unsigned char* bytes = (unsigned char*)ptr; 
for(int i = 0; i < size; i++) 
    // work with bytes[i] 

在這個例子中,bytes[i]等於*(bytes + i),它是用於訪問該存儲器地址:bytes + (i* sizeof(*bytes))。換句話說:如果你有int* intPtr和您嘗試訪問intPtr[1],你實際上是在訪問存儲在字節的整數:4到7:

0 1 2 3 
4 5 6 7 <-- 

鍵入您的指針指向的大小影響後,它指向遞增/遞減。所以如果你想逐字節地迭代你的數據,你需要一個指向1字節大小的指針(這就是爲什麼unsigned char*)。


unsigned char通常用於保存二進制數據,其中0有效價值,仍然是你的數據的一部分。在使用「裸體」unsigned char*時,您可能需要保存緩衝區的長度。

char通常用於保存代表字符串的字符,而0等於'\0'(終止字符)。如果您的字符緩衝區始終以'\0'終止,則不需要知道它的長度,因爲終止字符正好指定了數據的結尾。

請注意,在這兩種情況下,最好使用隱藏數據的內部表示的一些對象,併爲您負責內存管理(請參閱RAII idiom)。因此,使用std::vector<unsigned char>(用於二進制數據)或std::string(用於字符串)更好。

2

unsinged char類型通常用於表示二進制數據的單個byte。因此,數組通常用作二進制數據緩衝區,其中每個元素都是一個單字節字節。

unsigned char*構造將是一個指向二進制數據緩衝區(或其第一個元素)的指針。

我不是100%確定c++標準關於unsigned char的大小究竟是什麼意思,是否固定爲8位。 通常是是。我會盡力找到併發布它。

看到你的代碼

當你使用類似void* input作爲函數的參數之後,你故意剝離下來大約輸入原始類型的信息。這是非常強烈的建議,意見將以非常一般的方式處理。即作爲任意字節的字符串。另一方面,int* input會表明它會被視爲一個「串」的單一整數。

void*主要用於輸入被編碼的情況,或者由於某種原因而對其進行處理,因爲無法得出關於其內容的結論。

然後在你的函數中,你似乎想把輸入視爲一個字節串。 但是對物體進行操作,例如,執行operator=(賦值)編譯器需要知道該怎麼做。由於您聲明輸入爲void*,因此如*input = something這樣的分配將沒有意義,因爲*inputvoid類型。爲了讓編譯器將input元素當作「最小原始內存塊」處理,請將其轉換爲unsigned int的相應類型。

由於錯誤或意外的類型轉換,cout可能不起作用。 char*被認爲是以空字符結尾的字符串,並且很容易在代碼中混淆singedunsigned版本。如果您通過unsinged char*ostream::operator<<作爲char*它將視爲並期望byte輸入爲普通ASCII字符,其中0意味着字符串的結尾,而不是整數值0的整數值。當你想打印內存的內容時,最好顯式地投射指針。

另請注意,要打印緩衝區的內存內容,您需要使用循環,因爲其他方式打印功能不知道何時停止。

+1

C和C++定義字符類型('char','unsigned char'和'signed char')的大小爲一個字節,並要求它們至少有8位。有,或者至少直到最近纔有一臺具有9位「char」的機器,並且有一些具有32位字符。 (從歷史上看,有很多機器的字節少於8位,但C不允許這樣做。) –

+0

@詹姆斯,謝謝。我提到它,因爲我記得一些關於不能保證它總是8位的東西。我希望保持清楚,以防萬一實施一些低級網絡協議或將二進制文件從系統移到系統,他們可能會遇到這樣的警告。 – luk32

+1

很大程度上取決於你的便攜性。對於大多數人來說,可移植性約束將足夠寬鬆,以允許「char」是8位的假設,但是在那裏它不是機器。 –

7

在C中,unsigned char是唯一保證沒有陷印值的類型,並且保證複製將導致精確的按位圖像。 (C++也將此保證擴展到char)。由於這個原因,它傳統上用於「原始存儲器」(例如,memcpy的語義根據unsigned char定義)。

另外,通常使用無符號整數類型時,將使用按位運算(&,|,>>等)。 unsigned char是最小的無符號整數類型,可用於處理使用按位運算的小值數組。偶爾也會使用它,因爲在溢出的情況下需要模數行爲,但對於較大的類型(例如,計算散列值時)更頻繁。這兩個原因一般適用於無符號類型;當需要減少內存使用時,unsigned char通常僅用於它們。

+1

「C++將此保證延伸到'char'。」 - 我們可以有這個來源嗎? – emlai

0

當您想要逐字節訪問數據時,無符號字符指針非常有用。例如,一個函數,從一個區域到另一個副本的數據可能需要這樣的:

void memcpy (unsigned char* dest, unsigned char* source, unsigned count) 
{ 
    for (unsigned i = 0; i < count; i++) 
     dest[i] = source[i]; 
} 

它也有一個事實,即字節是內存的最小可尋址單元做。如果要讀取小於內存中某個字節的任何內容,則需要獲取包含該信息的字節,然後使用位操作選擇信息。

您可以非常好地使用int指針複製上述函數中的數據,但會複製4個字節的塊,這在某些情況下可能不是正確的行爲。

當您嘗試使用cout時,爲什麼屏幕上沒有任何內容出現,最可能的解釋是數據以零字符開頭,在C++中標記爲字符串的末尾。

+0

如果它仍然以0字符開始,它應該打印其他3個字符的值。如果在for循環中(int i = 0; i

+0

「你可以很好地使用'int'指針複製上述函數中的數據」不,你可以不用!除了'unsigned char'(我認爲_especially_ signed類型)之外的類型不能保證(A)覆蓋底層內存的所有位,或者(B)允許試圖重新解釋任意字節時可能產生的陷印/ int's。在這裏使用除'unsigned char *'以外的指針本質上是非常不可移植的。實現可能將其用作平臺相關的細節,但用戶不應該這樣做。 –