2013-02-02 16 views
6

Ho molte funzioni che prevedono una stringa come argomento, per cui utilizzo char*, ma tutte le mie funzioni che prevedono un array di byte, utilizzano anche char*.Distingue tra stringhe e array di byte?

Il problema è che posso facilmente commettere l'errore di passare un array di byte in una funzione stringa, causando tutti i tipi di overflow, perché non è possibile trovare il terminatore null.

In che cosa si verifica di solito questo? Posso immaginare di cambiare tutte le mie funzioni di array di byte per prendere uno uint8_t, e quindi il compilatore avviserà della firma quando passo una stringa. O qual è il giusto approccio qui?

+0

Creare un wrapper per un array di byte ?? –

+0

@VaughanHilts Non vedo come questo risolva il mio problema? – Muis

+0

Una stringa * è * una matrice di byte. Poiché non è possibile passare effettivamente gli array in C, ma solo un puntatore al primo elemento, in genere è necessario anche passare una dimensione. Basta controllare se la matrice contiene un valore zero. Se lo fa, allora è una "stringa". Altrimenti, non lo è. –

risposta

1

Il problema è più generale in C che si sta pensando. Poiché char* e char[] sono equivalenti ai parametri di funzione, tale parametro può menzionare tre diversi concetti semantici:

  • un puntatore su un char oggetto (questa è la definizione "ufficiale" dei tipi di puntatore)
  • un char matrice
  • una stringa

nella maggior parte dei casi in cui è possibile interfacce Mondern nello standard C utilizza void* un ar byte tipizzato ray, e probabilmente dovresti aderire a questa convenzione e usare char* solo per le stringhe.

char[] da soli probabilmente sono usati raramente in quanto tali; Non riesco a immaginare molti casi d'uso per questi. Se li consideri come numeri dovresti usare la variante signed o unsigned, se li vedi come bit pattern unsigned char dovrebbe essere la tua scelta.

Se davvero intende un array come parametro di funzione (char o no) è possibile contrassegnare questo fatto per il lettore occasionale del codice indicando chiaramente:

void toto(size_t n, char A[const n]); 

Ciò equivale a

void toto(size_t n, char *const A); 

ma rende più chiara la tua intenzione. E in futuro potrebbero esserci anche strumenti che controllano i limiti per te.

2

io generalmente fa un qualcosa di matrice come il seguente

typedef struct { 
    unsigned char* data; 
    unsigned long length; 
    unsigned long max_length; 
} array_t; 

poi passare array_t * intorno

e creare funzioni di matrice che si array_t *

void array_create(array_t* a, unsgined long length) // allocates memory, sets the max_length, zero length 

void array_add(array_t* a, unsigned char byte) // add a byte 

ecc

+1

probabilmente sarebbe meglio usare 'size_t' invece di' unsigned long' –

+0

sì, hai ragione –

0

Write una struttura comune per gestire sia la stringa che i byte.

struct str_or_byte 
{ 
    int type; 
    union 
    { 
     char *buf; 
     char *str; 
    }pointer; 
    int buf_length; 
} 

Se type non è stringa quindi accedere alla pointer.buf solo fino buf_length. In caso contrario, accedere direttamente a pointer.str senza controllare buf_length e mantenerlo come stringa terminata da null.

Oppure mantenere la stringa anche come array di byte considerando solo la lunghezza, non conservare il charatter terminato da null per la stringa.

E non utilizzare le funzioni di manoling della stringa che non considerano la lunghezza. Ciò significa utilizzare strncpy, strncat, strncmp ... invece di strcpy, strcat, strcmp ...

0

C utilizzo convenzione. Ecco le regole che uso (modellato dopo la std lib)

void foo(char* a_string); 

void bar(void* a_byte_array, size_t number_of_bytes_in_the_array); 

Questo è facile da ricordare. Se si passa un singolo char * ptr, DEVE essere un array di caratteri con terminazione null.