2013-02-08 13 views
7

A cosa servono i puntatori unsigned char? Ho visto in molti punti che il puntatore è di tipo cast per puntare a unsinged char Perché lo facciamo?Quando utilizzare il puntatore del char non firmato

Riceviamo un puntatore a int e quindi digitiamo il cast in unsigned char*. Ma se proviamo a stampare elementi in quell'array usando cout, non stampa nulla. perché? Non capisco. Sono nuovo di C++.

EDIT codice di esempio si

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

durante la conversione in un puntatore di caratteri, il primo byte è probabilmente zero, che è lo stesso del terminatore di stringa, quindi non verrà stampato nulla. Sarebbe di grande aiuto se potessi mostrare ciò che fai davvero, cioè pubblicare un po 'di codice. Si prega di creare un [SSCCE] (http://sscce.org/) e aggiungere alla domanda. –

+0

Ma penso che perderebbero le informazioni se il primo byte è zero e in realtà sto provando a stampare tutti e quattro i byte ma non sta stampando nulla. –

+2

La tua domanda sembra più sul "perché" piuttosto che sul "quando". Molto spesso, 'char unsigned * 'viene utilizzato come metodo di accesso a livello di byte per raggiungere in una variabile o indirizzo di memoria di un tipo altrimenti più formale. Ha molte sottigliezze, tra le quali, l'immunità alle rigide regole di aliasing e l'allineamento standard garantito con qualsiasi indirizzo tu ci passi. Nuovo in C++ non dovrebbe complicare questo compito se hai una certa familiarità con C. New to * programming *, vedo che questa è una sfida da comprendere. Forse hai qualche codice e un'idea dietro di esso hai domande? – WhozCraig

risposta

5

In realtà si sta cercando pointer arithmetic:

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

In questo esempio, bytes[i] è uguale a *(bytes + i) e viene utilizzato per accedere alla memoria del indirizzo: bytes + (i* sizeof(*bytes)). In altre parole: se si dispone int* intPtr e si tenta di accedere intPtr[1], in realtà si sta accedendo al numero intero conservato a byte: 4-7:

0 1 2 3 
4 5 6 7 <-- 

La dimensione del tipo tuoi punti puntatore ad colpisce dove fa dopo che è incrementato/decrementato. Quindi, se vuoi iterare i tuoi dati byte per byte, devi avere un puntatore per tipo di dimensione 1 byte (ecco perché unsigned char*).


unsigned char di solito è usato per contenere dati binari dove 0 è valore valido e ancora parte dei tuoi dati. Mentre lavori con "naked" unsigned char* probabilmente dovrai mantenere la lunghezza del tuo buffer.

char viene in genere utilizzato per contenere caratteri che rappresentano stringa e 0 è uguale a '\0' (carattere di chiusura). Se il buffer di caratteri viene sempre terminato con '\0', non è necessario conoscere la lunghezza perché il carattere di terminazione specifica esattamente la fine dei dati.

Si noti che in entrambi i casi è preferibile utilizzare alcuni oggetti che nascondono la rappresentazione interna dei dati e si prenderà cura della gestione della memoria (vedere RAII idiom). Quindi è molto meglio usare std::vector<unsigned char> (per dati binari) o std::string (per stringa).

2

Il tipo unsinged char viene in genere utilizzato come rappresentazione di un singolo byte di dati binari. Pertanto, e array viene spesso utilizzato come buffer di dati binari, in cui ogni elemento è un byte singolo.

Il costrutto unsigned char* sarà un puntatore al buffer di dati binari (o al suo primo elemento).

Non sono sicuro al 100% cosa dice esattamente lo standard c++ sulla dimensione di unsigned char, se è stato impostato su 8 bit o meno. Solitamente lo è. Proverò a trovarlo e a postarlo.

Dopo aver visto il codice

Quando si utilizza qualcosa come void* input come parametro di una funzione, si striscia deliberatamente verso il basso le informazioni relative ingressi tipo di originale. Questo è un suggerimento molto forte che l'input sarà trattato in modo molto generale. Cioè come una stringa arbitraria di byte. int* input d'altra parte suggerirebbe di essere trattato come una "stringa" di numeri interi singolati.

void* è usato soprattutto in casi in cui ingresso viene codificato, o trattati bit/byte saggio per qualsiasi motivo, dal momento che non è possibile trarre conclusioni circa il suo contenuto.

Quindi nella tua funzione sembra che tu voglia trattare l'input come una stringa di byte. Ma per operare su oggetti, ad es. eseguendo operator= (assegnazione) il compilatore deve sapere cosa fare. Dal momento che dichiarare l'input come assegnazione void* come *input = something non avrebbe senso poiché *input è di tipo void. Per fare in modo che il compilatore consideri gli elementi input come "i pezzi di memoria grezza più piccoli", li trasmetti al tipo appropriato che è unsigned int.

Il cout probabilmente non ha funzionato a causa di conversione di tipo errato o non intenzionale. char* è considerato una stringa terminata da null ed è facile confondere il codice di versione singed e unsigned. Se si passa ad unsinged char*ostream::operator<< come char* tratterà e si aspettano il byte ingresso come normali caratteri ASCII, dove 0 è destinata ad essere fine della stringa non un valore intero di 0. Quando si desidera stampare i contenuti della memoria, è meglio lanciare esplicitamente dei puntatori.

Si noti inoltre che per stampare il contenuto della memoria di un buffer è necessario utilizzare un ciclo, poiché altrimenti la funzione di stampa non saprebbe quando fermarsi.

+1

C e C++ definiscono i tipi di caratteri ('char',' unsigned char' e 'signed char') per avere una dimensione di un byte e richiedono che abbiano almeno 8 bit. C'è, o almeno fino a poco tempo fa, una macchina con 'char' a 9 bit, e ce ne sono alcuni con char a 32 bit. (Storicamente, naturalmente, c'erano un sacco di macchine con byte meno di 8 bit, ma C non lo consente.) –

+0

@James, grazie. L'ho menzionato, perché ricordo che non mi è stato garantito che si tratta sempre di 8 bit. Volevo stare alla larga nel caso in cui si implementassero alcuni protocolli di rete di basso livello o si spostassero file binari da un sistema a un sistema, si potrebbero verificare tali avvertimenti. – luk32

+1

Molto dipende da quanto portatile devi essere. Per la maggior parte delle persone, i vincoli di portabilità saranno abbastanza lenti da consentire l'ipotesi che 'char' sia 8 bit, ma ci sono macchine _are_ dove non lo sono. –

7

In C, unsigned char è l'unico tipo che non ha valori di trapping e garantisce che la copia genererà un'immagine bit a bit esatta. (C++ estende questa garanzia anche a char). Per questo motivo, viene tradizionalmente utilizzato per "memoria grezza" (ad esempio la semantica di memcpy è definita in termini di unsigned char).

Inoltre, senza segno tipi integrali in generale vengono utilizzati quando operazioni bit per bit (&, |, >> ecc) stanno per essere utilizzati. unsigned char è il tipo integrale senza segno più piccolo e può essere utilizzato durante la manipolazione di matrici di valori piccoli su cui vengono utilizzate operazioni bit a bit. Occasionalmente, viene anche utilizzato perché è necessario il comportamento del modulo in caso di overflow, sebbene questo sia più frequente con tipi più grandi (ad esempio quando si calcola un valore hash). Entrambe queste ragioni si applicano ai tipi non firmati in generale; unsigned char vengono normalmente utilizzati solo quando è necessario ridurre l'utilizzo della memoria.

+1

"C++ estende questa garanzia anche a' char'. " - Possiamo avere una fonte per questo? – emlai

0

I puntatori di caratteri senza segno sono utili quando si desidera accedere ai dati byte per byte. Ad esempio, una funzione che copia dati da un settore all'altro possono necessitare di questo:

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

Inoltre ha a che fare con il fatto che il byte è la più piccola unità indirizzabile di memoria.Se si desidera leggere qualcosa di più piccolo di un byte dalla memoria, è necessario ottenere il byte che contiene tali informazioni e quindi selezionare le informazioni utilizzando le operazioni bit.

Si potrebbe benissimo copiare i dati nella funzione sopra usando un puntatore int, ma quello copierebbe blocchi di 4 byte, il che potrebbe non essere il comportamento corretto in alcune situazioni.

Perché sullo schermo non viene visualizzato nulla quando si tenta di utilizzare cout, la spiegazione più probabile è che i dati inizino con un carattere zero, che in C++ segna la fine di una stringa di caratteri.

+0

Se inizia con 0 caratteri, dovrebbe comunque stampare il valore degli altri 3 caratteri. E se nel ciclo for nel codice per (int i = 0; i

+0

"Si potrebbe benissimo copiare i dati nella funzione precedente usando un puntatore" int' "" No, potresti benissimo _non_! I tipi ad eccezione di 'unsigned char' (& I think _especially_ signed types), non sono garantiti per (A) coprire tutti i bit della memoria sottostante o (B) consentire il trap/valori non validi che potrebbero derivare dal tentativo di reinterpretare byte arbitrari come' int's. L'uso di qualsiasi puntatore diverso da "unsigned char *" qui è intrinsecamente, e molto, non-portatile. Le implementazioni potrebbero usarlo come un dettaglio dipendente dalla piattaforma, ma gli utenti non dovrebbero. –

Problemi correlati