Search
malloc
realloc
'\n
struct
valgrind
tac
Program dekomponujeme na následující funkce.
char* read(FILE *fd);
char** read_lines(FILE *fd, size_t *n);
printf(“%s”)
int print_lines(size_t n, char **lines);
int print_lines(char **lines);
NULL-terminated
free_lines(char ***lines);
Implementační postup můžeme zvolit například následující.
FILE *fd = stdin;
FILE*
stdout
print
stdin
'\0
Příklad funkce print()
int print(char *line) { int r = 0; while (line && *line != '\0' && (r = putchar(*line++)) != EOF); return r == EOF ? ERROR_OUT : ERROR_OK; }
read
NULL
Příklad volání načtení vstupu a tisk
#include <stdlib.h> #include <stdio.h> void print(char *str); char* read(FILE *fd); int main(void) { int ret = EXIT_SUCCESS; FILE *fd = stdin; char *str = NULL; unsigned int count = 1; while ((str = read(fd))) { printf("%3d ", count++); print(str); free(str); } return ret; }
read()
read_lines
print_lines
Příklad volání read_lines(), print_lines() a free_lines()
int print_lines(size_t n, char **str); char** read_lines(FILE *fd, size_t *n); void free_lines(size_t n, char ***str); int main(void) { int ret = EXIT_SUCCESS; FILE *fd = stdin; size_t n = 0; char **lines = read_lines(fd, &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; }
fclose()
free()
Příklad zpracování argumentu programu
int main(int argc, char const *argv[] { FILE *fd = stdin; if (argc > 1) { fd = fopen(argv[1], "r"); } if (!fd) { fprintf(stderr, "ERROR: Cannot open input file\n"); fprintf(stderr, "Usage is %s [filename]\n", argv[0]); fprintf(stderr, "If filename is not given, stdin is used\n"); return ERROR_IN; } size_t m = 0; char **lines = read_lines(fd, &m); ... if (argc > 1) { fclose(fd); //close input if it is not the stdin } ...
error
errno
ERROR_OK
EXIT_SUCCESS
Příklad chybových kódu programu
enum { ERROR_OK = EXIT_SUCCESS, ERROR_IN = 100, ERROR_MEM = 101, ERROR_OUT = 102, };
Defince prototypů funkcí
char* read(FILE *fd, int *error); char** read_lines(FILE *fd, size_t *n, int *error);
print()
Příklad hlavičky funkcí print a read
int print(char *str); //return error value char* read(FILE *fd, int *error);
Příklad funkce print_lines
int print(char *line) { int r = 0; while (line && *line != '\0' && (r = putchar(*line++)) != EOF); return r == EOF ? ERROR_OUT : ERROR_OK; } int print_lines(size_t n, char **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! for (size_t i = n; ret == ERROR_OK && lines && i > 0; --i) { ret = print(lines[i-1]); } return ret; }
Jiný příklad tisku jednoho řádku
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
Kratší verze ternárním operátorem ? :
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; }
limit
ulimit
limits
$ tr -dc A-Za-z'\n' < /dev/urandom | head -c 20000000 > in-long.txt
libc
limits -v 2621440 ./main <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 ./main <in-long.txt; echo $? 101
stderr
int main(int argc, char const *argv[]) { ... print_error(ret); return ret; }
Například
void print_error(int error) { switch(error) { case ERROR_READ: fprintf(stderr, "ERROR: Reading input file\n"); break; case ERROR_MEM: fprintf(stderr, "ERROR: Memory allocation\n"); break; case ERROR_OUT: fprintf(stderr, "ERROR: Print the read input\n"); break; case ERROR_OK: default: //nothing to do break; } }
fprintf(stderr, "DEBUG: n: %lu size: %lu\n", *n, size);
limits -v 12582912 ./main <in-long.txt; echo $? ... DEBUG: n: 5921 size: 8192 DEBUG: n: 5922 size: 8192 101
head
wc
head -n 5922 in-long.txt |wc 5922 95927 742533
sysctl vm.overcommit_memory = 2
vm.overcommit_ratio
vm.overcommit_kbytes
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 ./main < 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) ./main < in-long.txt
unsigned int a[n];
ulimit -v 32768 ./main < 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) ./main < in-long.txt
fopen
fclose
getchar
putchar
getc
putc