2013-02-08 21 views
7

Jaka jest użyteczność wskaźników unsigned char? Widziałem go w wielu miejscach, że wskaźnik jest typem rzutowania na wskaźnik na unsinged char. Dlaczego to robimy?Kiedy używać niepodpisanego wskaźnika znakowego

Otrzymujemy wskaźnik do int, a następnie wpisz go do unsigned char*. Ale jeśli spróbujemy wydrukować element w tej tablicy za pomocą cout, to nic nie wydrukuje. czemu? Nie rozumiem. Jestem nowy w C++.

EDIT Przykładowy kod Poniżej

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

podczas konwersji do wskaźnika postaci, pierwszy bajt ma prawdopodobnie wartość zero, która jest taka sama jak terminator łańcucha, a więc nic nie zostanie wydrukowane. Pomogłoby to więcej, gdybyś mógł pokazać to, co naprawdę robisz, tj. Opublikować kod. Proszę zrób [SSCCE] (http://sscce.org/) i dodaj do pytania. –

+0

Ale myślę, że to utraciłoby informację, jeśli pierwszy bajt wynosi zero i faktycznie próbuję wydrukować wszystkie cztery bajty, ale to nie drukuje niczego. –

+2

Twoje pytanie dotyczy raczej "dlaczego", a nie "kiedy". Bardzo często "unsigned char *" jest używane jako metoda dostępu na poziomie bajta do osiągnięcia zmiennej lub adresu pamięci innego, bardziej formalnego typu. Ma wiele cech, między innymi odporność na rygorystyczne zasady aliasingu i gwarantowane w standardzie wyrównanie z jakimkolwiek adresem, który na nie rzucasz. Nowość w C++ nie powinna sprawić, że będzie to trudne, jeśli dobrze znasz C. Nowość w programowaniu *, postrzegam to jako wyzwanie do zrozumienia. Być może masz jakiś kod i pomysł, za którym masz pytania? – WhozCraig

Odpowiedz

5

jesteś rzeczywiście szuka pointer arithmetic:

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

W tym przykładzie bytes[i] jest równa *(bytes + i) i służy do uzyskania dostępu do pamięci na adres: bytes + (i* sizeof(*bytes)). Innymi słowy: Jeśli masz int* intPtr i próby uzyskania dostępu intPtr[1], w rzeczywistości dostępu do całkowitą zapisaną w bajtach: 4 do 7:

0 1 2 3 
4 5 6 7 <-- 

Wielkość wpisać swoje punkty wskaźnik do wpływa gdzie wskazuje po nim jest zwiększana/zmniejszana. Jeśli chcesz przetworzyć bajt danych po bajcie, musisz mieć wskaźnik do typu o rozmiarze 1 bajta (dlatego unsigned char*).


unsigned char jest zazwyczaj używany do przechowywania danych binarnych, gdzie 0 jest ważna wartość i nadal część danych. Podczas pracy z "nagim" unsigned char* prawdopodobnie będziesz musiał trzymać długość swojego bufora.

char jest zwykle używany do przechowywania znaków reprezentujących ciąg znaków i 0 jest równy '\0' (znak kończący). Jeśli Twój bufor znaków jest zawsze zakończony przez '\0', nie musisz znać jego długości, ponieważ znak kończący dokładnie określa koniec twoich danych.

Należy pamiętać, że w obu tych przypadkach lepiej jest użyć jakiegoś obiektu, który ukrywa wewnętrzną reprezentację danych i zajmie się zarządzaniem pamięcią dla ciebie (patrz RAII idiom). Więc lepiej jest użyć albo std::vector<unsigned char> (dla danych binarnych) lub std::string (dla łańcucha znaków).

2

Typ unsinged char jest zwykle używany jako reprezentacja pojedynczego byte danych binarnych. Zatem i tablica jest często używana jako binarny bufor danych, gdzie każdy element jest pojedynczym bajtem.

Konstrukcja unsigned char* będzie wskaźnikiem do bufora danych binarnych (lub jego pierwszego elementu).

Nie jestem w 100% pewny, co standard c++ mówi dokładnie o rozmiarze unsigned char, niezależnie od tego, czy jest ustawiony na 8-bitowy, czy nie. Zazwyczaj jest to. Postaram się go znaleźć i opublikować.

Po obejrzeniu kodu

Podczas korzystania coś podobnego void* input jako parametr funkcji, celowo rozebrać informacji o wejściach typu oryginału. Jest to bardzo silna sugestia, że ​​dane wejściowe będą traktowane w sposób bardzo ogólny. To znaczy. jako dowolny ciąg bajtów. int* input z drugiej strony sugerowałaby, że będzie traktowana jako "ciąg" pojedynczych liczb całkowitych.

void* jest stosowany głównie w przypadkach, gdy wejście zostanie zakodowany, lub traktowane bit/byte mądry z jakiegokolwiek powodu, ponieważ nie można wyciągnąć wnioski na temat jego zawartości.

Następnie w swojej funkcji wydaje się, że chcesz traktować wejście jako ciąg bajtów. Ale do działania na obiektach, np. wykonując operator= (zadanie) kompilator musi wiedzieć, co robić. Ponieważ deklarujesz dane wejściowe jako void*, takie przypisanie, jak *input = something, nie ma sensu, ponieważ *input ma typ void. Aby kompilator traktował elementy input jako "najmniejsze fragmenty nieprzetworzonej pamięci", należy go przesłać do odpowiedniego typu, który jest unsigned int.

Prawdopodobnie nie zadziałało cout z powodu niewłaściwej lub niezamierzonej konwersji typu. char* jest uważany za zakończony znakiem zero i łatwo jest pomylić kod wersji singed i unsigned. Jeśli podasz unsinged char* do jako char* będzie traktować i oczekiwać, że wejście byte jako normalne znaki ASCII, gdzie 0 ma być koniec ciąg nie liczba całkowita wartość 0. Gdy chcesz wydrukować zawartość pamięci, najlepiej rzucić okiem na wskaźniki.

Należy również pamiętać, że aby wydrukować zawartość pamięci bufora, należy użyć pętli, ponieważ inna funkcja drukowania nie wiedziałaby, kiedy zatrzymać.

+1

C i C++ definiują typy znaków ('char',' unsigned char' i 'signed char'), aby mieć rozmiar jednego bajtu i wymagają, aby miał co najmniej 8 bitów. Istnieje, a przynajmniej do niedawna była maszyna z 9-bitowym 'char', a niektóre z 32-bitowymi znakami. (Historycznie, oczywiście, było wiele maszyn z bajtami mniejszymi niż 8 bitów, ale C na to nie pozwala.) –

+0

@ James, dziękuję. Wspomniałem o tym, ponieważ pamiętam coś o tym, że nie ma gwarancji, że zawsze jest 8-bitowy. Chciałem pozostać czystym na wypadek, gdyby ktoś implementował jakieś protokoły sieciowe niskiego poziomu lub przenosił pliki binarne z systemu do systemu, mogą napotkać takie zastrzeżenia. – luk32

+1

Wiele zależy od przenośności. Dla większości ludzi ograniczenia przenośności będą na tyle luźne, aby pozwolić na założenie, że 'char' ma 8 bitów, ale tam są maszyny, gdzie nie jest. –

7

W języku C, unsigned char jest jedynym gwarantowanym typem, który nie ma wartości zalewkowania, a który gwarantuje kopiowanie da dokładny obraz bitowy. (C++ rozszerza tę gwarancję również na char). Z tego powodu jest tradycyjnie używany do "pamięci pierwotnej" (np. Semantyka memcpy jest zdefiniowana w terminach unsigned char).

Ponadto ogólnie używane typy bez znaku są używane, gdy używane są operacje bitowe (&, |, >>). unsigned char jest najmniejszym bezpisowym typem całki i może być używany podczas manipulowania tablicami małych wartości, w których używane są operacje bitowe. Czasami jest również używany, ponieważ w przypadku przepełnienia potrzebne jest zachowanie modulo, chociaż jest to częstsze w przypadku większych typów (na przykład przy obliczaniu wartości skrótu). Obie te przyczyny odnoszą się ogólnie do niepodpisanych typów; unsigned char będą normalnie używane tylko wtedy, gdy zajdzie potrzeba zmniejszenia użycia pamięci.

+1

"C++ również rozszerza tę gwarancję na' char'. " - Czy możemy mieć na to źródło? – emlai

0

Bez znaku wskaźniki char są przydatne, gdy chcesz uzyskać dostęp do bajtu danych przez bajt. Na przykład, funkcja, która kopiuje dane z jednego obszaru na inny może potrzebować to:

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

Posiada również do czynienia z faktem, że bajt jest najmniejsza adresowalna jednostka pamięci.Jeśli chcesz odczytać coś mniejszego niż bajt z pamięci, musisz zdobyć bajt zawierający te informacje, a następnie wybrać informacje za pomocą operacji bitowych.

Można bardzo dobrze skopiować dane w powyższej funkcji za pomocą wskaźnika int, ale skopiowałoby to fragmenty o wielkości 4 bajtów, co może nie być właściwym zachowaniem w niektórych sytuacjach.

Dlaczego nic nie pojawia się na ekranie przy próbie użycia cout, najbardziej prawdopodobne jest to, że dane zaczynają się od znaku zerowego, który w C++ oznacza koniec ciągu znaków.

+0

Jeśli zaczyna się od 0 znaków, powinien wydrukować wartość pozostałych 3 znaków. I jeśli w pętli for w kodzie dla (int i = 0; i

+0

"Można bardzo dobrze skopiować dane w powyższej funkcji za pomocą wskaźnika" int "" Nie, bardzo dobrze może _nie_! Typy z wyjątkiem 'unsigned char' (i myślę, że specjalnie podpisane typy), nie gwarantują (A) pokrywania wszystkich bitów podstawowej pamięci lub (B) pozwalają na wychwytywanie/nieprawidłowe wartości, które mogą wynikać z prób reinterpretacji arbitralnych bajtów jako' int. Używanie dowolnego wskaźnika innego niż "unsigned char *" jest z natury i nie jest przenośne. Implementacje mogą używać go jako szczegółów zależnych od platformy, ale użytkownicy nie powinni tego robić. –

Powiązane problemy