* pro vyučující: 06
malloc
a realloc
ve funkci read()
. Rozšíření na načítání více řádků je možné dokončit na 7. cvičení.
man 3 rand
.
'\n
'.
struct
, byť to může být výhodné).
valgrind
.
init
, print
, permute
, např.
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
.
#include <stdio.h> #include <stdlib.h> #ifndef MAX_NUM #define MAX_NUM 10 #endif 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]); int main(void) { int ret = EXIT_SUCCESS; const unsigned int n = MAX_NUM; unsigned int a[MAX_NUM]; init(n, a); print(n, a); permute(n, a); printf("Randomized array\n"); print(n, a); return 0; }
S výstupem např.
clang -DMAX_NUM=5 perm.c -o perm && ./perm 0 1 2 3 4 Randomized array 3 0 4 2 1
stdin
a jeho vytištění na stdoout
funkce print
.
'\0
'.
print
předpokládá, že arugment je platný ukazatel na paměť zakončenou znakem '\0
'. To musíme zajistit programově, korektní incializací a předáváním proměnných.
void print(char *str) { size_t i = 0; while (str && str[i] != '\0') { putchar(str[i++]); } // if (str && str[i] == '\0') putchar('\n'); //not needed if new line character '\n' is a part of the string str. }
read
korektně ošetřuje možné chybové stavy načtení a alokace paměti.
read
hodnotu neplatného ukazatele NULL
.
#include <stdlib.h> #include <stdio.h> #ifndef INIT_SIZE #define INIT_SIZE 128 #endif void print(char *str); char* read(void); int main(void) { int ret = EXIT_SUCCESS; char *str = NULL; unsigned int count = 1; while ((str = read())) { printf("%3d ", count++); print(str); free(str); } return ret; }
read()
použijeme dynamickou alokaci (volání malloc
) a realokaci (volání realloc
) s kontrolou úspěšného přidělení paměti.
'\0
') voláním realloc
.
read
.
read_lines
a print_lines
.
int print_lines(size_t n, char **str); char** read_lines(size_t *n); void free_lines(size_t n, char ***str); int main(void) { int ret = EXIT_SUCCESS; size_t n = 0; char **lines = read_lines(&n); print_lines(n, lines); free_lines(n, &lines); return ret; } void print_lines(size_t n, char **str) { if (str) { for (size_t i = 0; i < n; ++i) { print(str[i]); } } } 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; }
int main(void) { int ret = EXIT_SUCCESS; size_t n = 0; char **lines = read_lines(&n); unsigned int a[n]; // Počet řádku se musí vejít do paměti (zásobníku) init(n, a); permute(n, a); for (int i = 0; i < n; ++i) { print(lines[a[i]]); } free_lines(n, &lines); return ret; }
error
. (podobně funguje errno
ve std. knihovně C).
ERROR_OK
jako EXIT_SUCCESS
, abychom unifikovali identifikátory.
enum { ERROR_OK = EXIT_SUCCESS, ERROR_IN = 100, ERROR_MEM = 101, ERROR_OUT = 102, };
error
. Při chybě vrací NULL
, kterým nerozlišíme důvod chyby.
char* read(int *error); char** read_lines(size_t *n, int *error);
int print(char *str); //return error value char* read(int *error);
int print_lines(size_t n, char **str) { int ret = ERROR_OK; if (str) { for (size_t i = 0; i < n && ret == ERROR_OK; ++i) { ret = print(str[i]); } } return ret; }
int print(char *str) { int ret = ERROR_OK; size_t i = 0; while (str && str[i] != '\0') { if (putchar(str[i++]) == EOF) { ret = ERROR_OUT; break; } } return ret; }
break
s využitím ternárního operátor.
int print(char *str) { int ret = ERROR_OK; size_t i = 0; while (str && ret == ERROR_OK && str[i] != '\0') { ret = putchar(str[i++]) == EOF) ? ERROR_OUT : ret; } return ret; }
read
a read_lines
.
limit
, ulimit
nebo limits
, dle OS a použitého interpretu příkazů.
libc
limits -v 2621440 ./rand <in-long.txt; echo $? ld-elf.so.1: /lib/libc.so.7: mmap of entire address space failed: Cannot allocate memory 1
101
.
limits -v 5242880 ./rand <in-long.txt; echo $? 101
stderr
.
int main(void) { ... print_error(ret); return ret; }
fprintf(stderr, "DEBUG: n: %lu size: %lu\n", *n, size);
limits -v 5242880 ./rand <in-long.txt; echo $? 101
limits -v 12582912 ./rand <in-long.txt; echo $? ... DEBUG: n: 5921 size: 8192 DEBUG: n: 5922 size: 8192 101
head
a spočítáme řádky, slova a znaky programem wc
.
head -n 5922 in-long.txt |wc 5922 95927 742533
malloc
, které spíše vrátí přidělenou paměť (z virtuálního adreseního prostoru, který je v případě 64-bit dostatečně veliký) nicméně k fyzickému přidělení paměti dojde až při zápisu do paměti, které v případě nedostatku paměti skončí ukončením programu. Toto tzv. overcommit chování je možné nastavit globálně sysctl vm.overcommit_memory = 2
a následně vm.overcommit_ratio
a vm.overcommit_kbytes
, což však může mít katastrofální důsledky pro běžící OS.
read_lines
ladící výpis o pokusu rozšíření paměti, např. následovně.
char** read_lines(size_t *n, int *error) { size_t size = INIT_SIZE; char **lines = malloc(size * sizeof(char*)); //Assume INIT_SIZE would always be > 0 *n = 0; *error = ERROR_OK; //assume everything would be fine if (lines) { char *str; while ((str = read(error))) { //read would return NULL and set the error if (*n == size) { fprintf(stderr, "DEBUG: realloc from %lu -> %lu\n", size, size * 2); char **t = realloc(lines, sizeof(char*) * size * 2); ...
ulimit -v 26624 ./rand < in-long.txt DEBUG: realloc from 128 -> 256 DEBUG: realloc from 256 -> 512 DEBUG: realloc from 512 -> 1024 DEBUG: realloc from 1024 -> 2048 DEBUG: realloc from 2048 -> 4096 DEBUG: realloc from 4096 -> 8192 DEBUG: realloc from 8192 -> 16384 DEBUG: realloc from 16384 -> 32768 DEBUG: realloc from 32768 -> 65536 DEBUG: realloc from 65536 -> 131072 zsh: segmentation fault (core dumped) ./rand < in-long.txt
read()
, podařilo se nám tak načíst pouze část vstupu.
unsigned int a[n];
a omezené velikosti zásobníku.
ulimit -v 32768 ./rand < in-long.txt DEBUG: realloc from 128 -> 256 DEBUG: realloc from 256 -> 512 DEBUG: realloc from 512 -> 1024 DEBUG: realloc from 1024 -> 2048 DEBUG: realloc from 2048 -> 4096 DEBUG: realloc from 4096 -> 8192 DEBUG: realloc from 8192 -> 16384 DEBUG: realloc from 16384 -> 32768 DEBUG: realloc from 32768 -> 65536 DEBUG: realloc from 65536 -> 131072 DEBUG: realloc from 131072 -> 262144 zsh: segmentation fault (core dumped) ./rand < in-long.txt
stdin
a stdout
společně s dostatečnými zdroj. Spíše můžeme realizovat např. omezený diskový prostor při ukládání na disk nebo vzdálené síťové uložiště, což vyžaduje hlubší znalost používání OS.
fopen
, fclose
a náhradou getchar
a putchar
funkcemi pro práci s proudem getc
a putc
.