2013-02-02 19 views
6

Mam wiele funkcji, które oczekują ciąg jako argument, dla którego używam char*, ale wszystkie moje funkcje, które oczekują tablicy bajtowej, również używać char*.Rozróżnianie ciągów znaków i tablic bajtów?

Problem polega na tym, że mogę łatwo popełnić błąd przekazywania tablicy bajtów w funkcji łańcuchowej, powodując wszelkiego rodzaju przepełnienia, ponieważ nie można znaleźć terminatora o wartości NULL.

W jaki sposób jest to zwykle powiązane? Mogę sobie wyobrazić zmianę wszystkich funkcji tablic bajtowych, aby uzyskać uint8_t, a następnie kompilator będzie ostrzegał o podpisie, gdy przekażę ciąg. A co jest właściwym podejściem?

+0

Zrób opakowanie dla tablicy bajtów ?? –

+0

@VaughanHilts Nie rozumiem, jak to rozwiązuje mój problem? – Muis

+0

Łańcuch * to * tablica bajtów. Ponieważ w rzeczywistości nie można przekazać tablic w C, a jedynie wskaźnik do pierwszego elementu, zwykle trzeba również przekazać rozmiar. Po prostu sprawdź, czy tablica zawiera zerową wartość. Jeśli tak, to jest to "ciąg". W przeciwnym razie nie jest. –

Odpowiedz

1

Problem jest bardziej ogólny C niż myślisz. Od char* i char[] są równoważne dla parametrów funkcji, taki parametr może odnosić się do trzech różnych pojęć semantycznych:

  • wskaźnik na jednym char obiektu (jest to „oficjalna” definicja typów wskaźnikowych)
  • char tablica
  • ciąg

W większości przypadków, w których jest to możliwe, interfejsy Mondern w standardzie C wykorzystuje void* dla bez typu bajtów AR ray i prawdopodobnie powinieneś stosować się do tej konwencji i używać char* tylko dla łańcuchów.

char[] same z siebie prawdopodobnie są rzadko używane jako takie; Nie mogę sobie wyobrazić wielu przypadków użycia. Jeśli uważasz je za liczby, powinieneś użyć wariantu signed lub unsigned, jeśli widzisz je jako wzór bitowy, to powinien być twój wybór.

Jeśli naprawdę myśli tablicę jako parametr funkcji (char lub nie) można zaznaczyć ten fakt dla zwykłego czytelnika kodzie przez wyraźnie wskazując go:

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

Jest to odpowiednik

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

, ale zapewnia wyraźniejszą intencję. A w przyszłości mogą istnieć nawet narzędzia, które sprawdzą dla ciebie granice.

2

Generalnie zrób coś tablica jak na poniższym

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

następnie przekazać array_t * wokół

i tworzyć funkcje tablicowe, które biorą 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 

itp

+1

byłoby lepiej użyć 'size_t' zamiast' unsigned long' –

+0

tak, masz rację –

0

Write wspólna struktura do obsługi zarówno ciągu, jak i bajtów.

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

Jeśli type nie jest ciąg następnie uzyskać dostęp do pointer.buf tylko do połowy buf_length. W przeciwnym razie uzyskaj bezpośredni dostęp do pointer.str bez sprawdzania buf_length i zachowaj go jako łańcuch zakończony wartością NUL.

Albo utrzymuj ciąg również jako tablicę bajtów, biorąc pod uwagę tylko długość, nie pozostawiaj znaku zakończonego znakiem null dla łańcucha znaków.

struct str_or_byte 
{ 
    char *buf; 
    int buf_length; 
} 

I nie używaj funkcji manuplacji ciągów, które nie uwzględniają długości. Oznacza to, że korzystać strncpy, strncat, strncmp ... zamiast strcpy, strcat, strcmp ...

0

C używanie konwencji. Oto zasady, których używam (stworzone po std lib):

void foo(char* a_string); 

void bar(void* a_byte_array, size_t number_of_bytes_in_the_array); 

To jest łatwe do zapamiętania. Jeśli podajesz pojedynczy znak * ptr, MUSI to być tablica znaków zakończona znakiem NUL.

Powiązane problemy