Zadania
Dla zadania algorithm.h
zdefiniowano pakiet testów: test_algorithm.c
. Możesz go wykorzystać by zweryfikować tworzone przez siebie implementacje.
-
algorithm.h
,algorithm.c
: rozszerz bibliotekę algorithm (z poprzednich zajęć) o:-
funkcję
int* da_index(int **array, int *size, int index);
, która:-
dla wywołania
int *a = NULL, n = 0; da_index(&a, &n, k);
zwróci wskaźnik do elementu o indeksiek
wewnątrz nowo stworzonej tablicy dynamicznej, a zmiennea
in
będą wskazywały odpowiednio początek tablicy i rozmiar -
dla kolejnych wywołań
da_index(&a, &n, k)
zwróci wskaźnik do wcześniej istniejącego elementu jeślik < n
; w przeciwnym wypadku (k >= n
) powiększy rozmiar tablicy (wykorzystując funkcjęrealloc
) i odpowiednio zmodyfikuje zmiennea
in
-
wykorzystuje w swojej implementacji funkcję
realloc
-
nowo zaalokowane fragmenty tablicy są zaincjalizowane wartością 0
-
(opcjonalnie) wykorzystuje optymalne rozmiary pamięci w przypadku konieczności realokacji
-
-
-
text.c
: przygotuj program, który przyjmuje podkomendę, a następnie ciąg znaków. W zależności od podkomendy:-
bytes
: wyznacza długość tekstu w bajtach (wykorzystującstrlen
) -
length
: wyznacza długość tekstu w znakach UTF-8 (wykorzystując algorytm opisany poniżej) -
words
: liczy wyrazy (wykorzystującstrpbrk
,strspn
) -
tokenize
: dzieli tekst na tokeny (słowa rozdzielone spacją lub znakiem interpunkcyjnym (wykorzystującstrtok
)
-
Przykładowe wywołania text.c
:
$ ./text bytes "Kraków"
7
$ ./text length "Kraków"
6
$ ./text words "Kraków to miasto w Polsce"
5
$ ./text tokenize "Kraków to miasto w Polsce"
Kraków
to
miasto
w
Polsce
$ ./text tokenize "Moje ulubione liczby to 10,20,30"
Moje
ulubione
liczby
to
10
20
30
Alokacja pamięci
-
malloc(rozmiar)
- alokujerozmiar
charów (bajtów) i zwraca wskaźnik na zaalokowaną pamięć (lubNULL
) -
free(wskaźnik)
- dealokuje dynamicznie zaalokowaną pamięć -
realloc(wskaźnik, nowy_rozmiar)
- próbuje rozszerzyć pamięć donowy_rozmiar
- jeśli nie uda się zrobić tego w miejscu to alokuje nową pamięć o rozmiarzenowy_rozmiar
, kopiuje zawartość starej do nowej, dealokuje starą i zwraca wskaźnik na nową -
calloc(liczba, rozmiar)
- alokuje pamięć dla tablicyliczba
elementowej, w której każdy element ma rozmiarrozmiar
Należy manualnie określać rozmiar pamięci! Jeśli potrzebujemy 1 zmienną liczbową typu int, to alokujemy malloc(sizeof(int))
!
Przykład:
-
alokacja 30 elementowej tablicy liczb:
int *xs = malloc(sizeof(int) * 30); assert(xs != NULL);
-
realokacja do 20:
int* xs2 = realloc(xs, sizeof(int) * 20); assert(xs2 != NULL);
-
zwolnienie pamięci:
free(xs2);
Łańcuchy znaków (ang. strings)
Literały łańcuchów znaków w języku C są wskaźnikami na pierwszy znak łańcucha.
Zakończone są zawsze znakiem '\0'
, oznaczającym koniec łańcucha.
Jest to dzisiaj forma używana wyłącznie przez język C - większość popularnych języków programowania reprezentuje łańcuchy znaków jako parę wskaźnik + długość.
Literały łańcuchów znaków zazwyczaj są zaalokowane w pamięci globalnej programu przez kompilator - są dostępne wtedy przez cały czas życia programu. Można jednak wybrać lokalizację łańcucha przez przypisanie do deklaracji tablicy:
#include <stdio.h>
void test()
{
char local[] = "goodbye";
printf("test global: %p\n", "hello, world");
printf("test local: %p\n", local);
}
int main()
{
char local[] = "goodbye";
printf("main global: %p\n", "hello, world");
printf("main local: %p\n", local);
test();
return 0;
}
Trzecim miejscem przechowywania stringów są tworzone w ramach potrzeb tablice dynamiczne. Wykorzystują ten mechanizm m.in. wbudowane funkcje operujące na ciągach znaków w języku C:
#include <stdio.h>
#include <string.h>
int main()
{
char *foo = strdup("foo");
printf("%s\n", foo); // wypisuje foofoo
free(foo);
return 0;
}
Sekwencje ucieczki (ang. escape sequences)
Unicode
Standard określający liczbową wartość dla znaków z alfabetów świata. Jest rozszerzeniem standardu ASCII.
UTF-8
Uproszczone sprawdzanie długości tekstu UTF-8
-
Wczytaj bajt (
uint8_t
)-
Zwiększ licznik o 1
-
Jeśli bajt <= 127 jest to znak ASCII zajmujący 1 bajt. Znak = bajt. Przejdź do kroku 1
-
Jeśli bajt <= 223 jest to znak Unicode zajmujący 2 bajty. Wczytaj kolejny bajt, sprawdź czy jest mniejszy od 191 i przejdź do kroku 1
-
Jeśli bajt <= 239 jest to znak Unicode zajmujący 3 bajty. Wczytaj 2 kolejne bajty, sprawdź czy oba są mniejsze od 191 i przejdź do kroku 1
-
Wczytaj 3 kolejne bajty, sprawdź czy każdy z nich jest mniejszy od 191 i przejdź do kroku 1
-
Licznik = długość tekstu
Opis dokładny
Konwencja: 10xxxxxxx
oznacza, że dwa bity początkowe mają wartość 10
, a kolejne mają wartość dowolną.
Wczytaj bajt (uint8_t
) i spróbuj przypasować go do jednego z wzorców pierwszego bajtu.
Na podstawie wybranego wzorca wczytaj kolejne n kontynuacji, które odpowiada wspólnemu wzorcowi kontynuacji 10xxxxxxx
.
Wzorzec pierwszego bajtu | Liczba kontynuacji |
---|---|
0xxxxxxx |
0 |
110xxxxx |
1 |
1110xxxx |
2 |
11110xxx |
3 |
Znaki x
oznaczają bity wartości znaku - zapisz je w jeden ciąg bitów by odczytać zapisany znak.
Argumenty programu w C
Aby uzyskać dostęp do argumentów programu należy zdefiniować argumenty funkcji main
.
Zwyczajowe nazwy to argc
odpowiadające liczbie argumentów oraz argv
będące tablicą łańcuchów znaków.
Program wypisujący przekazane argumenty:
#include <stdio.h>
int main(int argc, char **argv)
{
for (int i = 0; i < argc; ++i) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
Obsługa plików
-
FILE *fopen(char const* filename, char const* mode);
- funkcja pozwalająca na otworzenie pliku. Zwraca NULL w przypadku błędu otwarcia pliku. -
int fclose(FILE *f);
- funkcja zamykająca plik -
fprintf
- odpowiednik printf, przyjmujący jako pierwszy argument dostęp do pliku, potem jak printf -
fscanf
- odpowiednik scanf, przyjmujący jako pierwszy argument dostęp do pliku, potem jak scanf -
fwrite
- funkcja pozwalająca na zapis do pliku -
fread
- funkcja pozwalająca na odczyt z pliku
Należy pamiętać o manalnym zamknięciu!