Search
'\n
\n
struct
enum { ERROR_OK = EXIT_SUCCESS, ERROR_READ = 100, ERROR_MEM = 101, ERROR_OUT = 102, }; // from stdin reads one line (ends \n); returns pointer to read array char* read(FILE *fd, int *error); char **read_lines(FILE *fd, size_t *n, int *error); int print(char *line); int print_lines(size_t n, char **lines); void free_lines(size_t n, char ***lines); void print_error(int error);
free_lines
NULL
Příklad implementace free_lines()
void free_lines(size_t n, char ***str) { if (str && *str) { for (int i = 0; i < n; ++i) { free((*str)[i]); } free(*str); } *str = NULL; }
Proměnnou s počtem řádků a polem s řádky
size_t n = 0; char ** lines = read_lines(fd, &n, &ret);
nahradíme
složeným typem struct
typedef struct lines { size_t n; char **lines; } lines_s; struct lines* read_lines(FILE *fd, int *error);
Složený typ alokujeme ve funkci read_lines(), nicméně z důvodu dynamické alokace lines→lines implementujeme alokaci složeného typu v samostatné funkce allocate_lines()
read_lines()
lines→lines
allocate_lines()
např.
static struct lines* allocate_lines(size_t sz) { struct lines *lines = malloc(sizeof(struct lines)); if (lines) { lines->n = 0; lines->lines = malloc(sizeof(char*) * sz); if (!lines->lines) { free(lines); // allocation fail lines = NULL; } } return lines; }
Funkci použijeme
např
Kromě funkce read_lines(), ve které alokujeme proměnnou složeného typu struct linies modifikujeme
struct linies
print_lines()
int print_lines(struct lines *lines);
např. na
int print_lines(struct lines *lines) { int ret = ERROR_OK; // Since i is of the size_t type, we might have to avoid "underflow" to negative values. Note that over/underflow of unsigned integers is defined in C/C++; however, for educative purposes, think about it! if (lines) { // we protect access to lines->n for (size_t i = lines->n; ret == ERROR_OK && i > 0; --i) { ret = print(lines->lines[i-1]); //we rely on correct data structure lines } } return ret; }
free_lines()
void free_lines(struct lines **lines);
např. jako
void free_lines(struct lines **lines) { if (lines && *lines) { for (size_t i = 0; (*lines)->lines && i < (*lines)->n; ++i) { if ((*lines)->lines[i]) { free((*lines)->lines[i]); } } free((*lines)->lines); free(*lines); *lines = NULL; } }
Příklad možných změn vůči řešení lab06
man 3 rand
init
print
permute
#ifndef MAX_NUM #define MAX_NUM 10 #endif #define SEED 42 void init(unsigned int n, unsigned int a[n]); void print(unsigned int n, unsigned int a[n]); void permute(unsigned int n, unsigned int a[n]);
stdout
Implementace init() a print() je relativně
init()
print()
přímočará
void init(unsigned int n, unsigned int a[n]) { for (unsigned int i = 0; i < n; i++) { a[i] = i; } } void print(unsigned int n, unsigned int a[n]) { for (unsigned int i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); }
Ve funkci permute() můžeme inicializovat generátor pseudonáhodných čísel pevných číslem, nebo aktuálním časem time().
permute()
time()
void permute(unsigned int n, unsigned int a[n]) { //srand(SEED); srand(time(NULL)); unsigned int d = 0; unsigned int tmp = 0; for (unsigned int i = n; i > 0; i--) { d = rand() % i; // swap tmp = a[d]; a[d] = a[i-1]; a[i-1] = tmp; } }
Příklad hlavní funkce main
int main(int argc, char const *argv[]) { unsigned int n = MAX_NUM; unsigned int a[n]; init(n, a); print(n, a); permute(n, a); print(n, a); }
Příklad výstupu s definováním délky sekvence při kompilaci
clang -DMAX_NUM=5 perm.c -o perm && ./perm 0 1 2 3 4 Randomized array 3 0 4 2 1
Vytvoření náhodné permutace využijeme k výpisu řádků v náhodném pořadí. Z důvodu použití typu size_t zaměníme předchozí typ unsigned int právě za size_t.
size_t
unsigned int
Použití size_t
void init(size_t n, size_t a[n]); void permute(size_t n, size_t a[n]);
Například ve funkci print_lines_rand()
int print_lines_rand(struct lines *lines) { int ret = ERROR_OK; if (lines) { // we protect access to lines->n size_t perm[lines->n]; init(lines->n, perm); permute(lines->n, perm); for (size_t i = lines->n; ret == ERROR_OK && i > 0; --i) { ret = print(lines->lines[perm[i-1]]); } } return ret; }
Alternativně můžeme pole náhodných čísel alokovat dynamicky
size_t* init(size_t n); void permute(size_t n, size_t *a); size_t* init(size_t n) { size_t *a = malloc(sizeof(size_t) * n); if (a) { for (size_t i = 0; i < n; i++) { a[i] = i; } } return a; } void permute(size_t n, size_t *a) { //srand(SEED); srand(time(NULL)); size_t d = 0; size_t tmp = 0; for (size_t i = n; i > 0; --i) { d = rand() % i; // However, function rand() returns int!!! // swap tmp = a[d]; a[d] = a[i-1]; a[i-1] = tmp; } } int print_lines_rand(struct lines *lines) { int ret = ERROR_OK; if (lines) { // we protect access to lines->n size_t *perm = init(lines->n); if (!perm) { return ERROR_MEM; } permute(lines->n, perm); for (size_t i = lines->n; ret == ERROR_OK && i > 0; --i) { ret = print(lines->lines[perm[i-1]]); } free(perm); } return ret; }
perm()
rand()
int
INT_MAX
“rand”
print_lines_rand()
Příklad ukazatele na funkci
int (*print_l)(struct lines *) = print_lines; //pointer to a function
Hodnotu ukazatele na funkci nastavíme na print_lines_rand, pokud je zadán druhý argument a jeho hodnota je “rand”, což ověříme funkcí strcmp() z knihovny <string.h>.
print_lines_rand
strcmp()
<string.h>
Ověření hodnoty druhého argumentu programu
if (argc > 2 && strcmp(argv[2], "rand") == 0) { print_l = print_lines_rand; // print lines in random order }
Volání funkce zajistíme přes ukazatel
if (fd) { struct lines *lines = read_lines(fd, &ret); if (lines) { ret = print_l(lines); // calling function according to the value of print_l free_lines(&lines); } if (argc > 1) { fclose(fd); } } else if (argc > 1) { ret = ERROR_READ; }
Program můžeme dále rozšířit o výpis chyby v případně, že druhý argument nemá hodnotu “rand”.