C/Napisy - własna implementacja: Różnice pomiędzy wersjami
literówka |
komentarze, zmiejszenie zmiennej is_equal do 1 B |
||
Linia 40: | Linia 40: | ||
void wpisz_do_str(const char *new_string) |
void wpisz_do_str(const char *new_string) |
||
{ |
{ |
||
for(;new_string && *new_string;++new_string) |
for(;new_string && *new_string;++new_string) //pętla po zwykłym napisie |
||
{ |
{ |
||
string new_char = create_string(""); |
string new_char = create_string(""); //tworzymy pusty napis |
||
new_char->c = *new_string; |
new_char->c = *new_string; //ustawiamy znak |
||
new_char->next = NULL; |
new_char->next = NULL; //zaznaczamy koniec napisu |
||
str->next = new_char; |
str->next = new_char; //dodajemy znak na końcu napisu |
||
} |
} |
||
} |
} |
||
Linia 55: | Linia 55: | ||
string create_string(const char *initial) |
string create_string(const char *initial) |
||
{ |
{ |
||
string new_string = malloc(sizeof *new_string); |
string new_string = malloc(sizeof *new_string); /* alokacja napisu */ |
||
new_string->wypisz = wypisz_str; /* ustawienie wskaźników na metody */ |
new_string->wypisz = wypisz_str; /* ustawienie wskaźników na metody */ |
||
new_string->wpisz = wpisz_do_str; |
new_string->wpisz = wpisz_do_str; |
||
for(;initial && *initial;++initial) /* |
for(;initial && *initial;++initial) /* pętla inicjalizująca (po zwykłym napisie) */ |
||
{ |
{ |
||
struct _string new_char; |
struct _string new_char; /* nowy napis */ |
||
new_char |
new_char.c = *new_string; /* znak */ |
||
new_char->next = NULL; |
new_char->next = NULL; /* koniec napisu */ |
||
tmp->next = new_char; |
tmp->next = new_char; /* dodanie znaku na końcu napisu */ |
||
} |
} |
||
str = new_string; /* ustawienie adresu ostatniego napisu */ |
str = new_string; /* ustawienie adresu ostatniego napisu */ |
||
return new_string; |
return new_string; /* zwrócenie wskaźnika do napisu */ |
||
} |
} |
||
/* destruktor */ |
/* destruktor */ |
||
void free_string(string *s) |
void free_string(string *s) |
||
{ |
{ |
||
if(*s == NULL)return; |
if(*s == NULL)return; /* nie zwalniamy pustych napisów */ |
||
if((*s)->next)free_string(s->next); |
if((*s)->next)free_string(s->next); /* rekurencja - usuwamy kolejny napis */ |
||
free(*s); |
free(*s); /* zwalniamy cały napis */ |
||
str = NULL; |
str = NULL; /* wyzerowanie wskaźnika na ostatni napis */ |
||
} |
} |
||
</source> |
</source> |
||
Linia 80: | Linia 80: | ||
=== Porównywanie === |
=== Porównywanie === |
||
<source lang="c"> |
<source lang="c"> |
||
char porownaj_str(string porownywany) |
|||
{ |
{ |
||
char is_equal; |
|||
string wsk = str; |
string wsk = str; |
||
for(;wsk != NULL;wsk = wsk->next) |
for(;wsk != NULL;wsk = wsk->next) |
Wersja z 13:13, 17 wrz 2012
Własna implementacja
Ponieważ tablice znaków mają ograniczenia, możemy zaimplementować łańcuch typu linked list.
Uwaga!
|
Typ danych
Typ zdefiniujemy jako klasę.
typedef struct _string
{
/* aktualny znak */
char c;
/* następny znak */
struct _string *next;
} *string;
Zauważmy, że łańcuch automatycznie oznaczamy jako wskaźnik. W ten sposób zabezpieczamy się przed kopiowaniami.
Metody
Na początek zajmiemy się wypisywaniem łańcucha:
typedef struct _string
{
FILE *(*wypisz)(FILE*); /* wskaźnik na metodę */
} *string;
FILE *wypisz_str(FILE *strum) /* metoda */
{
string wsk = str;
for(;wsk != NULL;wsk = wsk->next) /* pętla po znakach */
fputc(strum, wsk->c); /* wypisz znak */
}
Teraz zajmijmy się przypisaniem:
typedef struct _string
{ char c;
struct _string *next;
FILE *(*wypisz)(FILE*);
void (*wpisz)(const char*);
} *string;
static string str;
void wpisz_do_str(const char *new_string)
{
for(;new_string && *new_string;++new_string) //pętla po zwykłym napisie
{
string new_char = create_string(""); //tworzymy pusty napis
new_char->c = *new_string; //ustawiamy znak
new_char->next = NULL; //zaznaczamy koniec napisu
str->next = new_char; //dodajemy znak na końcu napisu
}
}
Dla uproszczenia zapisu skorzystaliśmy z konstruktora klasy string. Tego jeszcze nie mamy, więc czas na niego:
/* konstruktor */
string create_string(const char *initial)
{
string new_string = malloc(sizeof *new_string); /* alokacja napisu */
new_string->wypisz = wypisz_str; /* ustawienie wskaźników na metody */
new_string->wpisz = wpisz_do_str;
for(;initial && *initial;++initial) /* pętla inicjalizująca (po zwykłym napisie) */
{
struct _string new_char; /* nowy napis */
new_char.c = *new_string; /* znak */
new_char->next = NULL; /* koniec napisu */
tmp->next = new_char; /* dodanie znaku na końcu napisu */
}
str = new_string; /* ustawienie adresu ostatniego napisu */
return new_string; /* zwrócenie wskaźnika do napisu */
}
/* destruktor */
void free_string(string *s)
{
if(*s == NULL)return; /* nie zwalniamy pustych napisów */
if((*s)->next)free_string(s->next); /* rekurencja - usuwamy kolejny napis */
free(*s); /* zwalniamy cały napis */
str = NULL; /* wyzerowanie wskaźnika na ostatni napis */
}
Porównywanie
char porownaj_str(string porownywany)
{
char is_equal;
string wsk = str;
for(;wsk != NULL;wsk = wsk->next)
for(;porownywany != NULL;porownywany = porownywany->next)
if(wsk->c == porownywany->c)
is_equal = 1;
else
is_equal = 0;
return is_equal;
}
Konwersje
Pora na konwersje. Można je zaimplementować analogicznie do sprintf i sscanf.
Jak komputer przechowuje w pamięci listę znaków?
W pamięci komputera najpierw stoi "głowa" łańcucha (pierwszy znak).
Sytuacja taka jest przedstawiona poniżej (łańcuch "Hello"):
0x0329adf9382 c 'H' 0x0329adf9382 next 0x0329adf9383 0x0329adf9383 c 'e' 0x0329adf9383 next 0x0329adf9384 0x0329adf9384 c 'l' 0x0329adf9384 next 0x0329adf9385 0x0329adf9385 c 'l' 0x0329adf9385 next 0x0329adf9386 0x0329adf9386 c 'o' 0x0329adf9386 next 0x00000000
Uwaga!
|
Na samym końcu stoi "ogon" (ostatni znak).
Ideą listy znaków jest to, że może się rozszerzać, bez specyfikowania żadnego wymiaru "na sztywno". Inną jej cechą jest to, iż nie możemy jej indeksować.