Difference between revisions of "2016-17 Programmi C"
m |
(→Programs using libraries: Added program using pipe.) |
||
(50 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
+ | = C language - No Libraries = | ||
+ | |||
+ | The following programs do not use library functions (only printf, just to have a feedback of the results). | ||
+ | |||
+ | == arrays, pointers and structs == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | char *spoint="hello"; | ||
+ | char sarr[]="hello"; | ||
+ | struct strs { | ||
+ | char s[6]; | ||
+ | } sstruct = {"hello"}; | ||
+ | |||
+ | void foo(char *s) { | ||
+ | s[4]=0; | ||
+ | } | ||
+ | |||
+ | void bar(struct strs s) { | ||
+ | s.s[4]=0; | ||
+ | printf("from bar %s\n", s.s); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | printf("%s %s %s\n", spoint, sarr, sstruct.s); | ||
+ | foo(sarr); | ||
+ | printf("%s %s %s\n", spoint, sarr, sstruct.s); | ||
+ | bar(sstruct); | ||
+ | printf("%s %s %s\n", spoint, sarr, sstruct.s); | ||
+ | // test the following statements, one at a time | ||
+ | //spoint = sarr; | ||
+ | //sarr = spoint; | ||
+ | foo(spoint); | ||
+ | printf("%s %s %s\n", spoint, sarr, sstruct.s); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == iteration and recursion == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct elem { | ||
+ | int val; | ||
+ | struct elem *next; | ||
+ | }; | ||
+ | |||
+ | struct elem *head = NULL; | ||
+ | |||
+ | struct elem *rinsert(struct elem *new, struct elem *head) { | ||
+ | if (head == NULL || new->val < head->val) { | ||
+ | new->next = head; | ||
+ | return new; | ||
+ | } else { | ||
+ | head->next = rinsert(new, head->next); | ||
+ | return head; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | struct elem *iinsert(struct elem *new, struct elem *head) { | ||
+ | struct elem **pnext; | ||
+ | for (pnext = &head; | ||
+ | *pnext != NULL && new->val > (*pnext)->val; | ||
+ | pnext = &((*pnext)->next)) | ||
+ | ; | ||
+ | new->next = *pnext; | ||
+ | *pnext = new; | ||
+ | return head; | ||
+ | } | ||
+ | |||
+ | void rprint(struct elem *this) { | ||
+ | if (this) { | ||
+ | printf("%d ",this->val); | ||
+ | rprint(this->next); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void iprint(struct elem *this) { | ||
+ | for ( ; this != NULL; this = this->next) | ||
+ | printf("%d ",this->val); | ||
+ | } | ||
+ | |||
+ | struct elem test[]={{5},{3},{9},{1},{7}}; | ||
+ | #define NELEM (sizeof(test) / sizeof(struct elem)) | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | int i; | ||
+ | for (i = 0; i < NELEM; i++) | ||
+ | head = rinsert(&test[i], head); | ||
+ | rprint(head); | ||
+ | printf("\n"); | ||
+ | iprint(head); | ||
+ | printf("\n"); | ||
+ | head = NULL; | ||
+ | for (i = 0; i < NELEM; i++) | ||
+ | head = iinsert(&test[i], head); | ||
+ | rprint(head); | ||
+ | printf("\n"); | ||
+ | iprint(head); | ||
+ | printf("\n"); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == comma operator == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int slen(char *s) { /* in real programs use strlen instead */ | ||
+ | size_t rval; | ||
+ | for (rval = 0; *s != 0; s++, rval++) | ||
+ | ; | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | int ispal(char *s) { | ||
+ | int i,j; | ||
+ | for (i=0, j=slen(s)-1; i < j; i++, j--) { | ||
+ | if (s[i] != s[j]) | ||
+ | return 0; | ||
+ | } | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | void reverse(char *s) { | ||
+ | int i,j; | ||
+ | for (i=0, j=slen(s)-1; i < j; i++, j--) | ||
+ | s[j] ^= s[i] ^= s[j] ^= s[i]; | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[1]) { | ||
+ | for ( ;argc > 1; argv++, argc--) { | ||
+ | printf("\"%s\" is%s palindrome\n", argv[1], | ||
+ | ispal(argv[1])?"":"n't"); | ||
+ | reverse(argv[1]); | ||
+ | printf("\"%s\"\n", argv[1]); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == string by value == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int slen(char *s) { /* in real programs use strlen instead */ | ||
+ | int rval; | ||
+ | for (rval = 0; *s != 0; s++, rval++) | ||
+ | ; | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | void printxvowel(char v, char *s) { | ||
+ | int s_len = slen(s); | ||
+ | char locals[s_len]; | ||
+ | int i; | ||
+ | for (i=0; i<s_len; i++) { | ||
+ | switch (s[i]) { | ||
+ | case 'a': | ||
+ | case 'e': | ||
+ | case 'i': | ||
+ | case 'o': | ||
+ | case 'u': | ||
+ | locals[i] = v; | ||
+ | break; | ||
+ | default: | ||
+ | locals[i] = s[i]; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | printf("-> %s\n<- %s\n",s,locals); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | for ( ; argc > 1; argc--, argv++) { | ||
+ | printxvowel('a',argv[1]); | ||
+ | printxvowel('e',argv[1]); | ||
+ | printxvowel('i',argv[1]); | ||
+ | printxvowel('o',argv[1]); | ||
+ | printxvowel('u',argv[1]); | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | test it using "Garibaldi fu ferito, fu ferito ad una gamba, Garibaldi che comanda, che comanda il battaglion" as argv[1]. | ||
+ | |||
+ | == tables and preprocessor tricks == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | #define rows_of(X) (sizeof(X) / sizeof((X)[0])) | ||
+ | |||
+ | #define printTable(X) do { \ | ||
+ | int i; \ | ||
+ | printf("TABLE " #X ": size of element %d\n" \ | ||
+ | "(printed by the line %d of source file %s)\n", \ | ||
+ | sizeof(*(table ## X)), __LINE__, __FILE__); \ | ||
+ | for (i = 0; i < rows_of(table ## X); i++) \ | ||
+ | printf(#X " %02d %s\n",i,table ## X [i]); \ | ||
+ | } while (0); | ||
+ | |||
+ | char tableA[][50] = {"Sempre caro mi fu quest'ermo colle,", | ||
+ | "e questa siepe, che da tanta parte", | ||
+ | "dell’ultimo orizzonte il guardo esclude."}; | ||
+ | char *tableB[] = {"Sempre caro mi fu quest'ermo colle,", | ||
+ | "e questa siepe, che da tanta parte", | ||
+ | "dell’ultimo orizzonte il guardo esclude."}; | ||
+ | |||
+ | int main(int argc, char *argv[1]) { | ||
+ | int i; | ||
+ | printTable(A); | ||
+ | printf("\n"); | ||
+ | printTable(B); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == arrays and pointers == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int slen(char *s) { /* in real programs use strlen instead */ | ||
+ | size_t rval; | ||
+ | for (rval = 0; *s != 0; s++, rval++) | ||
+ | ; | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | void echoargs(int argc, char *argv[]) { | ||
+ | int i; | ||
+ | for (i = 0; i < argc; i++) | ||
+ | printf("argv[%d] = \"%s\"\n",i,argv[i]); | ||
+ | printf("\n"); | ||
+ | } | ||
+ | |||
+ | enum state {SPACE, CHAR}; | ||
+ | |||
+ | int splitargv(char *s, char **argv) { | ||
+ | enum state state = SPACE; | ||
+ | int count = 0; | ||
+ | //until the string is finished | ||
+ | for (; *s != 0; s++) { | ||
+ | //if s point to a character that is a space or a tab or a new line | ||
+ | if (*s == ' ' || *s == '\t' || *s == '\n') { | ||
+ | if (state != SPACE) | ||
+ | count++; | ||
+ | if (argv != NULL) | ||
+ | //insert a zero for ending the string | ||
+ | *s = 0; | ||
+ | state = SPACE; | ||
+ | } else { | ||
+ | if (state == SPACE && argv != NULL) | ||
+ | //if it is in SPACE state but the pointed character is not a "space" it splits the string by setting that the next element of the argv array should point to the new string. | ||
+ | *argv++ = s; | ||
+ | state = CHAR; | ||
+ | } | ||
+ | }; | ||
+ | if (state != SPACE) | ||
+ | count++; | ||
+ | if (argv != NULL) | ||
+ | *argv = NULL; | ||
+ | return count; | ||
+ | } | ||
+ | |||
+ | void splitargs(char *args) { | ||
+ | int newargc = splitargv(args, NULL); | ||
+ | char *newargv[newargc + 1]; | ||
+ | splitargv(args, newargv); | ||
+ | echoargs(newargc, newargv); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[1]) { | ||
+ | echoargs(argc, argv); | ||
+ | for ( ; *argv != NULL; argv++) { | ||
+ | printf("Split \"%s\"\n",*argv); | ||
+ | splitargs(*argv); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This is a simplified version of the idea used in the [https://github.com/rd235/s2argv-execs libexecs] library. Test this program using args like: | ||
+ | <pre> | ||
+ | ./a.out "ciao mare" "a b c" | ||
+ | </pre> | ||
+ | This program split the strings contained in the argv array by truncating them on space, tab and new line characters. <br> | ||
+ | The new strings are inserted in the argv array and argc is increased properly. [[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) | ||
+ | |||
+ | == void * and function pointers == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | typedef void (*voidfun) (void *arg); | ||
+ | |||
+ | void printint(void *arg) { | ||
+ | int *iarg = arg; | ||
+ | printf("int %d\n", *iarg); | ||
+ | } | ||
+ | |||
+ | void printstring(void *arg) { | ||
+ | char *sarg = arg; | ||
+ | printf("int %s\n", sarg); | ||
+ | } | ||
+ | |||
+ | void printpointer(void *arg) { | ||
+ | printf("pointer %p\n", arg); | ||
+ | } | ||
+ | |||
+ | void printfun(void *arg) { | ||
+ | voidfun fun = arg; | ||
+ | fun((void *) 0x42); | ||
+ | } | ||
+ | |||
+ | void launch(voidfun f, void *opaque) { | ||
+ | f(opaque); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[1]) { | ||
+ | int v = 235; | ||
+ | char *s = "Lasciate ogni speranza, o voi ch'entrate"; | ||
+ | launch(printint, &v); | ||
+ | launch(printstring, s); | ||
+ | launch(printfun, printpointer); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This technique is used to implement callbacks with opaque args. | ||
+ | |||
+ | = Programs using libraries = | ||
+ | |||
+ | == char by char copy (stdio) == | ||
<syntaxhighlight lang=C> | <syntaxhighlight lang=C> | ||
#include <stdio.h> | #include <stdio.h> | ||
Line 7: | Line 340: | ||
putchar(c); | putchar(c); | ||
} | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == similar programs, different approaches (stdio, string, stdlib, ctype) == | ||
+ | |||
+ | First program: | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <ctype.h> | ||
+ | #include <string.h> | ||
+ | |||
+ | #define BUFLEN 4096 | ||
+ | |||
+ | char *loadfile(char *path) { | ||
+ | char *contents = NULL; | ||
+ | size_t contents_len = 0; | ||
+ | char buffer[BUFLEN]; | ||
+ | size_t len; | ||
+ | FILE *fin = fopen(path, "r"); | ||
+ | FILE *fout = open_memstream(&contents, &contents_len); | ||
+ | if (fin == NULL || fout == NULL) | ||
+ | exit(1); | ||
+ | while ((len = fread(buffer, sizeof(char), BUFLEN, fin)) > 0) | ||
+ | fwrite(buffer, sizeof(char), len, fout); | ||
+ | fclose(fin); | ||
+ | fclose(fout); | ||
+ | return(contents); | ||
+ | } | ||
+ | |||
+ | enum state {SPACE, CHAR}; | ||
+ | |||
+ | int splitwords(char *s, char **words) { | ||
+ | enum state state = SPACE; | ||
+ | int count = 0; | ||
+ | for (; *s != 0; s++) { | ||
+ | if (isalnum(*s)) { | ||
+ | if (state == SPACE && words != NULL) | ||
+ | *words++ = s; | ||
+ | state = CHAR; | ||
+ | } else { | ||
+ | if (state != SPACE) | ||
+ | count++; | ||
+ | if (words != NULL) | ||
+ | *s = 0; | ||
+ | state = SPACE; | ||
+ | } | ||
+ | }; | ||
+ | if (state != SPACE) | ||
+ | count++; | ||
+ | return count; | ||
+ | } | ||
+ | |||
+ | static int sortcmp(const void *p1, const void *p2) { | ||
+ | const char *s1 = *(char * const *) p1; | ||
+ | const char *s2 = *(char * const *) p2; | ||
+ | int rval = strlen(s1) - strlen(s2); | ||
+ | if (rval == 0) | ||
+ | rval = strcmp(s1, s2); | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[1]) { | ||
+ | if (argc == 2) { | ||
+ | char *contents = loadfile(argv[1]); | ||
+ | int count = splitwords(contents, NULL); | ||
+ | char **words = malloc(count * sizeof(char *)); | ||
+ | if (words == NULL) | ||
+ | return 1; | ||
+ | else { | ||
+ | int i; | ||
+ | splitwords(contents, words); | ||
+ | qsort(words, count, sizeof(char *), sortcmp); | ||
+ | for (i = 0; i < count; i++) | ||
+ | printf("%s\n", words[i]); | ||
+ | return 0; | ||
+ | } | ||
+ | } else | ||
+ | return 1; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Second program: | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <ctype.h> | ||
+ | #include <string.h> | ||
+ | |||
+ | struct word { | ||
+ | struct word *next; | ||
+ | int count; | ||
+ | char word[]; | ||
+ | }; | ||
+ | |||
+ | static int lenalphacmp(const char *s1, const char *s2) { | ||
+ | int rval = strlen(s1) - strlen(s2); | ||
+ | if (rval == 0) | ||
+ | rval = strcmp(s1, s2); | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | struct word *addword(char *word, struct word *wlist) { | ||
+ | struct word **scan; | ||
+ | int cmpvalue; | ||
+ | for (scan = &wlist; | ||
+ | *scan != NULL && ((cmpvalue = lenalphacmp(word, (*scan)->word)) > 0); | ||
+ | scan = &((*scan)->next)) | ||
+ | ; | ||
+ | if (scan != NULL && cmpvalue == 0) | ||
+ | (*scan)->count ++; | ||
+ | else { | ||
+ | struct word *new = malloc(sizeof(struct word) + strlen(word) + 1); | ||
+ | if (new) { | ||
+ | new->next = *scan; | ||
+ | new->count = 1; | ||
+ | strcpy(new->word, word); | ||
+ | *scan = new; | ||
+ | } | ||
+ | } | ||
+ | return wlist; | ||
+ | } | ||
+ | |||
+ | struct word *addwordlen(char *word, size_t len, struct word *wlist) { | ||
+ | char word0[len+1]; | ||
+ | strncpy(word0,word,len); | ||
+ | word0[len]=0; | ||
+ | return addword(word0, wlist); | ||
+ | } | ||
+ | |||
+ | struct word *processline(char *line, struct word *wlist) { | ||
+ | char *thisword = NULL; | ||
+ | for ( ;*line != 0; line++) { | ||
+ | if (isalnum(*line)) { | ||
+ | if (thisword == NULL) | ||
+ | thisword = line; | ||
+ | } else { | ||
+ | if (thisword != NULL) { | ||
+ | wlist = addwordlen(thisword, line - thisword, wlist); | ||
+ | thisword = NULL; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | return wlist; | ||
+ | } | ||
+ | |||
+ | void printwlist(struct word *wlist) { | ||
+ | for ( ; wlist != NULL; wlist = wlist->next) | ||
+ | printf("%s : %d\n", wlist->word, wlist->count); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[1]) { | ||
+ | if (argc == 2) { | ||
+ | FILE *fin = fopen(argv[1], "r"); | ||
+ | ssize_t len; | ||
+ | char *linebuf = NULL; | ||
+ | size_t linebuflen = 0; | ||
+ | struct word *wlist = NULL; | ||
+ | while ((len = getline(&linebuf, &linebuflen, fin)) > 0) | ||
+ | wlist = processline(linebuf, wlist); | ||
+ | fclose(fin); | ||
+ | free(linebuf); | ||
+ | printwlist(wlist); | ||
+ | return 0; | ||
+ | } else | ||
+ | return 1; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Command line args management (getopt) == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <getopt.h> | ||
+ | #include <libgen.h> | ||
+ | |||
+ | void usage_and_exit(char *progname) { | ||
+ | fprintf(stderr, | ||
+ | "Usage:\n" | ||
+ | " %s OPTIONS [value] ... [value]\n" | ||
+ | "Options:\n" | ||
+ | " -v | --verbose: verbose mode\n" | ||
+ | " -h | --help: print a short usage helping message\n" | ||
+ | " -a val | --add val add a value\n" | ||
+ | " -s val | --sub val subtract a value\n" | ||
+ | " --double double the final result\n" | ||
+ | "This program computes an integer value\n", progname); | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | static struct option long_options[] = { | ||
+ | {"verbose", no_argument, 0, 'v'}, | ||
+ | {"help", no_argument, 0, 'h'}, | ||
+ | {"add", required_argument, 0, 'a'}, | ||
+ | {"sub", required_argument, 0, 's'}, | ||
+ | {"double", no_argument, 0, 0x101}, | ||
+ | }; | ||
+ | static char *short_options = "vha:s:"; | ||
+ | |||
+ | int opt; | ||
+ | |||
+ | int value = 0; | ||
+ | int verbose = 0; | ||
+ | int doubleval = 1; | ||
+ | |||
+ | char *progname = basename(argv[0]); | ||
+ | |||
+ | while((opt = getopt_long(argc, argv, short_options, long_options, NULL)) > 0) { | ||
+ | switch (opt) { | ||
+ | case 'v': verbose = 1; | ||
+ | break; | ||
+ | case 'a': value += atoi(optarg); | ||
+ | break; | ||
+ | case 's': value -= atoi(optarg); | ||
+ | break; | ||
+ | case 0x101: doubleval *= 2; | ||
+ | break; | ||
+ | case 'h': | ||
+ | case '?': usage_and_exit(progname); | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | for (; optind < argc; optind++) | ||
+ | value += atoi(argv[optind]); | ||
+ | |||
+ | value *= doubleval; | ||
+ | |||
+ | if (verbose) | ||
+ | printf("the result is %d\n", value); | ||
+ | |||
+ | return value; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This program uses the getopt function for parsing the arguments. | ||
+ | Starting from a base value of 0, it gives the possibility to add a number, subtract it and multiply it by two. | ||
+ | The final value is the return code of the main or printed if verbose mode is activated. | ||
+ | |||
+ | == dynamic allocation on the heap, qsort, getline (stdlib) == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | |||
+ | #define AOS_LENSTEP 8 | ||
+ | |||
+ | /* | ||
+ | * Define a struct for managing and array of pointer to strings. | ||
+ | */ | ||
+ | struct array_of_strings { | ||
+ | char **strings; | ||
+ | size_t currentlength; //the length of number of string in memory | ||
+ | size_t arraylength; //the length of memory allocated | ||
+ | }; | ||
+ | |||
+ | typedef struct array_of_strings array_of_strings; | ||
+ | |||
+ | void array_of_strings_add(array_of_strings *arrayOfStrings, char *string) { | ||
+ | //if there is not enough space for the string increase it | ||
+ | if (arrayOfStrings->currentlength >= arrayOfStrings->arraylength) { | ||
+ | //increase the array length by the size of a string pointer | ||
+ | size_t newlength = arrayOfStrings->arraylength + AOS_LENSTEP; | ||
+ | //reallocate the arrayOfString with the new size | ||
+ | char **new_string = realloc(arrayOfStrings->strings, newlength * sizeof(arrayOfStrings->strings[0])); | ||
+ | //if the reallocation is successful | ||
+ | if (new_string != NULL) { | ||
+ | arrayOfStrings->arraylength = newlength; | ||
+ | arrayOfStrings->strings = new_string; | ||
+ | } | ||
+ | } | ||
+ | //if there is enough space for the string insert it | ||
+ | if (arrayOfStrings->currentlength < arrayOfStrings->arraylength) | ||
+ | //strdup return a pointer to a duplicate of the string | ||
+ | arrayOfStrings->strings[arrayOfStrings->currentlength++] = strdup(string); | ||
+ | } | ||
+ | |||
+ | void array_of_strings_print(array_of_strings *v) { | ||
+ | size_t i; | ||
+ | for (i = 0; i < v->currentlength; i++) | ||
+ | printf("[%3lu]: %s\n", i, v->strings[i]); | ||
+ | } | ||
+ | |||
+ | static int alphasort(const void *a, const void *b) { | ||
+ | return strcmp(*(char **) a, *(char **) b); | ||
+ | } | ||
+ | |||
+ | void array_of_strings_sort(array_of_strings *v) { | ||
+ | qsort(v->strings, v->currentlength, sizeof(v->strings[0]), alphasort); | ||
+ | } | ||
+ | |||
+ | void array_of_strings_free(array_of_strings *v) { | ||
+ | size_t i; | ||
+ | for (i = 0; i < v->currentlength; i++) | ||
+ | free(v->strings[i]); | ||
+ | if (v->arraylength > 0) | ||
+ | free(v->strings); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | char* line = NULL; | ||
+ | size_t lineLength = 0; | ||
+ | //size is signed but can store values [-1, SSIZE_MAX] | ||
+ | ssize_t numberOfCharactersRead; | ||
+ | static array_of_strings arrayOfStrings; | ||
+ | if (argc != 1) | ||
+ | exit(1); | ||
+ | //stdin is the standard input from terminal | ||
+ | while ((numberOfCharactersRead = getline(&line, &lineLength, stdin)) >= 0) { | ||
+ | if (line[numberOfCharactersRead - 1] == '\n') | ||
+ | line[numberOfCharactersRead - 1] = 0; | ||
+ | array_of_strings_add(&arrayOfStrings, line); | ||
+ | } | ||
+ | |||
+ | free(line); | ||
+ | array_of_strings_sort(&arrayOfStrings); | ||
+ | array_of_strings_print(&arrayOfStrings); | ||
+ | array_of_strings_free(&arrayOfStrings); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This program demonstrates the use of getline for reading an input stream, realloc for dynamic memory allocation and qsort for sorting an array of dynamic allocated array of strings. <br> | ||
+ | For executing this program is suggest to use input redirection by typing "program < inputfile". In this way the while loop will be stopped by reaching end of file. | ||
+ | |||
+ | == open_memstream (quite tricky) == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | |||
+ | static int alphasort(const void *a, const void *b) { | ||
+ | return strcmp(* (char **) a, * (char **) b); | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | char *line = NULL; | ||
+ | size_t lineLength = 0; | ||
+ | ssize_t numberOfCharacterRead; | ||
+ | FILE *fakefile; | ||
+ | char *fakeMemoryFile = NULL; | ||
+ | char **stringsarray; | ||
+ | size_t stringsArrayLength = 0; | ||
+ | char **scan; | ||
+ | char *dups; | ||
+ | if (argc != 1) | ||
+ | exit(1); | ||
+ | |||
+ | fakefile = open_memstream(&fakeMemoryFile, &stringsArrayLength); | ||
+ | if (fakefile == NULL) | ||
+ | exit(1); | ||
+ | |||
+ | while ((numberOfCharacterRead = getline(&line, &lineLength, stdin)) >= 0) { | ||
+ | if (line[numberOfCharacterRead - 1] == '\n') | ||
+ | line[numberOfCharacterRead - 1] = 0; | ||
+ | if ((dups = strdup(line)) != NULL) | ||
+ | //write the duplicated read line to the memory stream | ||
+ | fwrite(&dups, sizeof(dups), 1, fakefile); | ||
+ | } | ||
+ | |||
+ | free(line); | ||
+ | dups = NULL; | ||
+ | //write a null string at the end of the stream | ||
+ | fwrite(&dups, sizeof(dups), 1, fakefile); | ||
+ | fclose(fakefile); | ||
+ | stringsarray = (char **) fakeMemoryFile; | ||
+ | //obtain the number of strings contained in the array | ||
+ | stringsArrayLength /= sizeof(char *); | ||
+ | //ignore the ending null string | ||
+ | stringsArrayLength--; | ||
+ | printf("%lu\n",stringsArrayLength); | ||
+ | qsort(stringsarray, stringsArrayLength, sizeof(stringsarray[0]), alphasort); | ||
+ | for (scan = stringsarray; *scan != NULL; scan++) | ||
+ | printf("[%3lu]: %s\n", scan - stringsarray, *scan); | ||
+ | for (scan = stringsarray; *scan != NULL; scan++) | ||
+ | free(*scan); | ||
+ | free(stringsarray); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This program is similar to 2.4. But instead of using an array structure it use a file stored in memory that isn't stored in the hard drive. | ||
+ | |||
+ | == fantasy of strcpy (time, stdint, string) == | ||
+ | Cit. [http://www.liberliber.it/mediateca/libri/r/rostand/cirano_di_bergerac/pdf/cirano_p.pdf Cirano] page 33. | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdint.h> | ||
+ | #include <string.h> | ||
+ | #include <time.h> | ||
+ | |||
+ | char *strcpy0(char *dest, const char *src) { | ||
+ | int i; | ||
+ | int len; | ||
+ | //determine the length of src | ||
+ | for (len = 0; src[len] != 0; len++) | ||
+ | ; | ||
+ | //copy src into dest | ||
+ | for (i = 0; i <= len; i++) | ||
+ | dest[i] = src[i]; | ||
+ | return dest; | ||
+ | } | ||
+ | |||
+ | char *strcpy1(char *dest, const char *src) { | ||
+ | int i; | ||
+ | //set i as the lenth of src | ||
+ | for (i = 0; src[i] != 0; i++) | ||
+ | ; | ||
+ | //backward copy src into dest | ||
+ | for ( ; i >= 0; i--) | ||
+ | dest[i] = src[i]; | ||
+ | return dest; | ||
+ | } | ||
+ | |||
+ | char *strcpy2(char *dest, const char *src) { | ||
+ | char *rval = dest; | ||
+ | //copy the characters until the end of the string | ||
+ | while ((*dest++ = *src++) != 0) | ||
+ | ; | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Same as strcpy2 but with array format instead of pointer. Notice that this introduce an overhead due to calculating offset. | ||
+ | */ | ||
+ | char *strcpy3(char *dest, const char *src) { | ||
+ | int i; | ||
+ | for (i=0; (dest[i] = src[i]) != 0; i++) | ||
+ | ; | ||
+ | return dest; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Recursive copy of the string. | ||
+ | */ | ||
+ | char *strcpy4(char *dest, const char *src) { | ||
+ | if ((*dest = *src) != 0) | ||
+ | strcpy4(dest+1, src+1); | ||
+ | return dest; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Similar to strcpy2 | ||
+ | */ | ||
+ | char *strcpy5(char *dest, const char *src) { | ||
+ | char *rval = dest; | ||
+ | while (*src != 0) | ||
+ | *dest++ = *src++; | ||
+ | *dest = 0; | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This strcpy copy 64 bit at a time instead of 8 bit. | ||
+ | */ | ||
+ | char *strcpy6(char *dest, const char *src) { | ||
+ | //unsigned long long pointer | ||
+ | uint64_t *src64 = (uint64_t *) src; | ||
+ | uint64_t *dest64 = (uint64_t *) dest; | ||
+ | while (1) { | ||
+ | //says to the compiler to keep tmp in a register | ||
+ | register uint64_t tmp = *src64; | ||
+ | register uint64_t scan; | ||
+ | //detect if the current tmp cointans a 0 character | ||
+ | for (scan = 0xffULL; (tmp & scan) != 0; scan <<= 8) | ||
+ | ; | ||
+ | //printf("%llx %llx %p %p %s\n",scan, tmp, src64, dest64, (char *) src64); | ||
+ | //if the current tmp cointains a 0 character uses strcpy2 for wiser copy. | ||
+ | if (scan != 0) { | ||
+ | strcpy2((char *) dest64, (char *) src64); | ||
+ | break; | ||
+ | } | ||
+ | *dest64++ = tmp; | ||
+ | src64++; | ||
+ | } | ||
+ | return dest; | ||
+ | } | ||
+ | |||
+ | #define NUMBER_OF_TEST 1000000 | ||
+ | |||
+ | double time_strcpy(char * (*cpy)(char *, const char *), char *d, const char *s) | ||
+ | { | ||
+ | clock_t starttime, time; | ||
+ | int i; | ||
+ | starttime = clock(); | ||
+ | for (i = 0; i< NUMBER_OF_TEST; i++) | ||
+ | cpy(d, s); | ||
+ | time = clock() - starttime; | ||
+ | return (1.0) * time / CLOCKS_PER_SEC; | ||
+ | } | ||
+ | /** | ||
+ | * Calls the n-th version of the strcopy and calculates the execution of it. Prints the result. | ||
+ | */ | ||
+ | #define TESTSTRCPY(N,D,S) \ | ||
+ | printf("strcpy" #N " %s time %lf\n", strcpy ##N ((D),(S)), time_strcpy(strcpy ##N, (D),(S))) | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | if (argc != 2) | ||
+ | return 1; | ||
+ | else { | ||
+ | size_t argv1len=strlen(argv[1]); | ||
+ | char argcpy[argv1len+1]; | ||
+ | strncpy(argcpy, argv[1], argv1len); | ||
+ | printf("strncpy %s\n", argcpy); | ||
+ | TESTSTRCPY(, argcpy, argv[1]); | ||
+ | TESTSTRCPY(0, argcpy, argv[1]); | ||
+ | TESTSTRCPY(1, argcpy, argv[1]); | ||
+ | TESTSTRCPY(2, argcpy, argv[1]); | ||
+ | TESTSTRCPY(3, argcpy, argv[1]); | ||
+ | TESTSTRCPY(4, argcpy, argv[1]); | ||
+ | TESTSTRCPY(5, argcpy, argv[1]); | ||
+ | TESTSTRCPY(6, argcpy, argv[1]); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This program compares various homemade implementation of string copy function with the library one. | ||
+ | |||
+ | == math and printf == | ||
+ | |||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <unistd.h> | ||
+ | #include <math.h> | ||
+ | |||
+ | #define DELAY 10000 | ||
+ | #define DURATION 30 | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | double x; | ||
+ | int i; | ||
+ | for (i = 0; i < DURATION * 1000000 / DELAY; i++) { | ||
+ | double y = sin(M_PI * i / 100); | ||
+ | int pos = 40 + (40 * y); | ||
+ | //\r is carriage return \b move the character back one position. | ||
+ | printf("\r%*s\b",pos,"*"); | ||
+ | //print the remaing character of stream and clean it | ||
+ | fflush(stdout); | ||
+ | //wait a DELAY time | ||
+ | usleep(DELAY); | ||
+ | printf("\n"); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This program print a vertical sine curve on the terminal. It uses the math library for calculating the sine value and unistd for making the animation(with the use of usleep function). | ||
+ | |||
+ | == minishell (history and execs) == | ||
+ | ... questo esercizio richiede il caricamento di librerie da installare (history/readline da distribuzione, execs da github). | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <readline/readline.h> | ||
+ | #include <readline/history.h> | ||
+ | #include <execs.h> | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | while (1) { | ||
+ | //prompt the given string and read a line of text | ||
+ | char *cmd = readline("% "); | ||
+ | if (cmd == NULL) | ||
+ | break; | ||
+ | if (*cmd != 0) { | ||
+ | //add the string to the end of shell history | ||
+ | add_history(cmd); | ||
+ | //execute the command without starting a shell | ||
+ | system_nosh(cmd); | ||
+ | } | ||
+ | free(cmd); | ||
+ | } | ||
+ | printf("\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Per il linking dinamico della libreria execs consiglio di impostare la variabile d'ambiente LD_LIBRARY_PATH al giusto percorso tramite i comandi: | ||
+ | <syntaxhighlight lang=Bash> | ||
+ | LD_LIBRARY_PATH=/usr/local/lib | ||
+ | export LD_LIBRARY_PATH | ||
+ | </syntaxhighlight> | ||
+ | Questo programma legge una riga di testo e la esegue come comando. | ||
+ | |||
+ | == conversions (math) == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <math.h> | ||
+ | |||
+ | int main() { | ||
+ | char c; | ||
+ | short s; | ||
+ | int i; | ||
+ | long l; | ||
+ | long long ll; | ||
+ | unsigned char uc; | ||
+ | unsigned short us; | ||
+ | unsigned int ui; | ||
+ | unsigned long ul; | ||
+ | unsigned long long ull; | ||
+ | float f; | ||
+ | double d; | ||
+ | long double ld; | ||
+ | |||
+ | c = s = i = l = ll = 0xc1c2c3c4c5c6c7c8; | ||
+ | printf("%d %llx %lld\n", sizeof(ll), ll, ll); | ||
+ | printf("%d %lx %ld\n", sizeof(l), l, l); | ||
+ | printf("%d %x %d\n", sizeof(i), i, i); | ||
+ | printf("%d %x %d\n", sizeof(s), s, s); | ||
+ | printf("%d %x %d\n", sizeof(c), c, c); | ||
+ | |||
+ | ll = l = i = s = c; | ||
+ | printf("%d %x %d\n", sizeof(s), s, s); | ||
+ | printf("%d %x %d\n", sizeof(i), i, i); | ||
+ | printf("%d %lx %ld\n", sizeof(l), l, l); | ||
+ | printf("%d %llx %lld\n", sizeof(ll), ll, ll); | ||
+ | |||
+ | //set on i a 1 on the last left position | ||
+ | i = 1 << (sizeof(i) * 8 - 1); | ||
+ | printf("%d %x %d\n", sizeof(i), i, i); | ||
+ | i >>= 1; | ||
+ | printf("%d %x %d\n", sizeof(i), i, i); | ||
+ | i = -1; | ||
+ | printf("%d %x %d\n", sizeof(i), i, i); | ||
+ | i >>= 1; | ||
+ | printf("%d %x %d\n", sizeof(i), i, i); | ||
+ | |||
+ | uc = us = ui = ul = ull = 0xc1c2c3c4c5c6c7c8; | ||
+ | printf("%d %llx %llu\n", sizeof(ull), ull, ull); | ||
+ | printf("%d %lx %lu\n", sizeof(ul), ul, ul); | ||
+ | printf("%d %x %u\n", sizeof(ui), ui, ui); | ||
+ | printf("%d %x %u\n", sizeof(us), us, us); | ||
+ | printf("%d %x %u\n", sizeof(uc), uc, uc); | ||
+ | ull = ul = ui = us = uc; | ||
+ | printf("%d %x %u\n", sizeof(us), us, us); | ||
+ | printf("%d %x %u\n", sizeof(ui), ui, ui); | ||
+ | printf("%d %lx %lu\n", sizeof(ul), ul, ul); | ||
+ | printf("%d %llx %llu\n", sizeof(ull), ull, ull); | ||
+ | |||
+ | f = d = ld = acosl(-1); | ||
+ | printf("%2d %80.78llf\n", sizeof(ld), ld); | ||
+ | printf("%2d %80.78lf\n", sizeof(d), d); | ||
+ | printf("%2d %80.78f\n", sizeof(f), f); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Explanation of program's output for a [https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models LP64 architecture]: | ||
+ | <syntaxhighlight lang=text> | ||
+ | //size-hexadecimal-decimal | ||
+ | 8 c1c2c3c4c5c6c7c8 -4484807029008447544 //long long is equal to long in LP64 | ||
+ | 8 c1c2c3c4c5c6c7c8 -4484807029008447544 | ||
+ | 4 c5c6c7c8 -976828472 //first 4 byte truncated | ||
+ | 2 ffffc7c8 -14392 //the number is saved in two's complement so negative numbers have some 1 to the left. | ||
+ | 1 ffffffc8 -56 | ||
+ | 2 ffffffc8 -56 | ||
+ | 4 ffffffc8 -56 | ||
+ | 8 ffffffffffffffc8 -56 | ||
+ | 8 ffffffffffffffc8 -56 | ||
+ | 4 80000000 -2147483648 | ||
+ | 4 c0000000 -1073741824 //the left-most byte becomes 11000000 instead of 01000000 because the number was negative. | ||
+ | 4 ffffffff -1 //in two's complement the number with all 1 represents -1 | ||
+ | 4 ffffffff -1 //-1 is negative so right shifting it add a 1 to the left. | ||
+ | 8 c1c2c3c4c5c6c7c8 13961937044701104072 //unsigned type can hold higher values. | ||
+ | 8 c1c2c3c4c5c6c7c8 13961937044701104072 | ||
+ | 4 c5c6c7c8 3318138824 | ||
+ | 2 c7c8 51144 //this number is unsigned so showing it on two's complement doesn't include the 1 to the left. | ||
+ | 1 c8 200 | ||
+ | 2 c8 200 | ||
+ | 4 c8 200 | ||
+ | 8 c8 200 | ||
+ | 8 c8 200 | ||
+ | //various floating-point notations | ||
+ | 16 3.141592653589793238512808959406186204432742670178413391113281250000000000000000 //long double | ||
+ | 8 3.141592653589793115997963468544185161590576171875000000000000000000000000000000 //double | ||
+ | 4 3.141592741012573242187500000000000000000000000000000000000000000000000000000000 //float | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == A home-brewed mini-printf (variadic functions, indirect recursion) == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdarg.h> | ||
+ | |||
+ | int vrprintf(const char *format, va_list ap); | ||
+ | |||
+ | //static function are functions that are only visible to functions in this file | ||
+ | |||
+ | //writes the given character (as int) to the standard output it the character is not 0 | ||
+ | static int putcx(int c) { | ||
+ | if (c!=0) { | ||
+ | putchar(c); | ||
+ | return 1; | ||
+ | } else | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | static char backchar[128] = { | ||
+ | [0 ... 127] = 0, | ||
+ | ['a'] = '\a', | ||
+ | ['b'] = '\b', | ||
+ | ['f'] = '\f', | ||
+ | ['n'] = '\n', | ||
+ | ['r'] = '\r', | ||
+ | ['t'] = '\t', | ||
+ | ['v'] = '\v', | ||
+ | ['\\'] = '\\', // double escape character is needed for saving the escape character itself in a variable | ||
+ | ['\''] = '\'', //use the escape character for saving the apostrophe | ||
+ | ['"'] = '"', | ||
+ | ['?'] = '?', | ||
+ | }; | ||
+ | |||
+ | static int put_backslash(char escapeChar) { | ||
+ | //write the backslash | ||
+ | //the and-bitwise is for switch off the last bit (ASCII compatibility) | ||
+ | return putcx(backchar[escapeChar & 0x7f]); | ||
+ | } | ||
+ | |||
+ | static int rvrp_int(int val) { | ||
+ | if (val == 0) | ||
+ | return 0; | ||
+ | else //recurvise call on the more significant digit and print the less significant | ||
+ | return rvrp_int(val / 10) + putcx('0' + val % 10); | ||
+ | } | ||
+ | |||
+ | static int vrp_int(int val) { | ||
+ | if (val != 0) { | ||
+ | if (val < 0) | ||
+ | return putcx('-') + rvrp_int(-val); | ||
+ | else | ||
+ | return rvrp_int(val); | ||
+ | } else | ||
+ | return putcx('0'); | ||
+ | } | ||
+ | |||
+ | static int vrp_string(char *s) { | ||
+ | switch (*s) { | ||
+ | case 0: | ||
+ | return 0; | ||
+ | case '\\': | ||
+ | //if after a \ the string not ends recursive call on next character otherwise return 0 | ||
+ | return put_backslash(*(s+1)) ? vrp_string(s + 2) + 1 : 0; | ||
+ | default: | ||
+ | return putcx(*s) + vrp_string(s + 1); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | static int vrp_percent(const char *format, va_list ap) { | ||
+ | switch (*format) { | ||
+ | case 0: | ||
+ | return 0; | ||
+ | case '%': | ||
+ | //case when a % is followed by a % so print a % and advance | ||
+ | return putcx(*format) + vrprintf(format + 1, ap); | ||
+ | case 'd': | ||
+ | //call vrp_int with the next argument of the list with type int | ||
+ | return vrp_int(va_arg(ap, int)) + vrprintf(format + 1, ap); | ||
+ | case 's': | ||
+ | //call vrp_int with the next argument of the list with type pointer to char | ||
+ | return vrp_string(va_arg(ap, char *)) + vrprintf(format + 1, ap);; | ||
+ | default: | ||
+ | //if the format is not recognized | ||
+ | printf("ERROR\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int vrprintf(const char *format, va_list ap) { | ||
+ | //parse the format | ||
+ | switch (*format) { | ||
+ | //case format null | ||
+ | case 0: | ||
+ | return 0; | ||
+ | //case format %: call vrp_percent and advance 1 position | ||
+ | case '%': | ||
+ | return vrp_percent(format + 1, ap); | ||
+ | case '\\': | ||
+ | //if after a \ the string not ends recursive call on next character otherwise return 0 | ||
+ | return put_backslash(*(format+1)) ? vrprintf(format + 2, ap) + 1 : 0; | ||
+ | default: | ||
+ | //print the character and recursive call on next character | ||
+ | return putcx(*format) + vrprintf(format + 1, ap); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //function with undetermined number of arguments. Returns the number of characters printed. | ||
+ | int rprintf(const char *format, ...) { | ||
+ | int rval; | ||
+ | //declare a list of arguments | ||
+ | va_list ap; | ||
+ | //initialize the list | ||
+ | va_start (ap, format); | ||
+ | rval = vrprintf(format, ap); | ||
+ | //clean up the list | ||
+ | va_end(ap); | ||
+ | return rval; | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | int v; | ||
+ | v = rprintf("hello world\n"); | ||
+ | printf("%d\n", v); | ||
+ | v = printf("hello world\n"); | ||
+ | printf("%d\n", v); | ||
+ | v = rprintf("hello world %d\n", 10); | ||
+ | printf("%d\n", v); | ||
+ | v = printf("hello world %d\n", 10); | ||
+ | printf("%d\n", v); | ||
+ | v = rprintf("hello world %s %d\n", "piripicchio", 42); | ||
+ | printf("%d\n", v); | ||
+ | v = printf("hello world %s %d\n", "piripicchio", 42); | ||
+ | printf("%d\n", v); | ||
+ | v = rprintf("hello world %% \"%s\" %d\n", "piripicchio\tbackslash", 42); | ||
+ | printf("%d\n", v); | ||
+ | v = printf("hello world %% \"%s\" %d\n", "piripicchio\tbackslash", 42); | ||
+ | printf("%d\n", v); | ||
+ | v = rprintf("%%\n"); | ||
+ | printf("%d\n", v); | ||
+ | v = printf("%%\n"); | ||
+ | printf("%d\n", v); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | The program shows the use of indirect recursion (for example in rvrp_int e vvrp_int function) and variadic functions for emulate the printf. | ||
+ | |||
+ | == Linux kernel (alike) lists == | ||
+ | |||
+ | File listx.h: | ||
+ | <syntaxhighlight lang=C> | ||
+ | /* subset of the Linux Kernel source file: "include/linux/list.h" | ||
+ | CPLv2 */ | ||
+ | #ifndef _LISTX_H | ||
+ | #define _LISTX_H | ||
+ | |||
+ | #define NULL 0 | ||
+ | typedef unsigned int size_tt; | ||
+ | |||
+ | //macro for obtaining a pointer to the struct given a member of it | ||
+ | #define container_of(ptr, type, member) ({ \ | ||
+ | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ | ||
+ | (type *)( (char *)__mptr - offsetof(type,member) );}) | ||
+ | |||
+ | //macro for obtaining the offset of a struct's member. | ||
+ | #define offsetof(TYPE, MEMBER) ((size_tt) &((TYPE *)0)->MEMBER) | ||
+ | |||
+ | struct list_head { | ||
+ | struct list_head *next, *prev; | ||
+ | }; | ||
+ | |||
+ | #define LIST_HEAD_INIT(name) { &(name), &(name) } | ||
+ | |||
+ | #define LIST_HEAD(name) \ | ||
+ | struct list_head name = LIST_HEAD_INIT(name) | ||
+ | |||
+ | //when you declare a function 'inline' gcc integrate that function'c code into the code of its callers. | ||
+ | |||
+ | static inline void INIT_LIST_HEAD(struct list_head *list) | ||
+ | { | ||
+ | //circular list, so when it's empty next and previus point to list | ||
+ | list->next = list; | ||
+ | list->prev = list; | ||
+ | } | ||
+ | static inline void __list_add(struct list_head *new, | ||
+ | struct list_head *prev, | ||
+ | struct list_head *next) | ||
+ | { | ||
+ | next->prev = new; | ||
+ | new->next = next; | ||
+ | new->prev = prev; | ||
+ | prev->next = new; | ||
+ | } | ||
+ | static inline void list_add(struct list_head *new, struct list_head *head) | ||
+ | { | ||
+ | __list_add(new, head, head->next); | ||
+ | } | ||
+ | static inline void list_add_tail(struct list_head *new, struct list_head *head) | ||
+ | { | ||
+ | __list_add(new, head->prev, head); | ||
+ | } | ||
+ | static inline void __list_del(struct list_head * prev, struct list_head * next) | ||
+ | { | ||
+ | next->prev = prev; | ||
+ | prev->next = next; | ||
+ | } | ||
+ | static inline void list_del(struct list_head *entry) | ||
+ | { | ||
+ | __list_del(entry->prev, entry->next); | ||
+ | } | ||
+ | static inline int list_is_last(const struct list_head *list, | ||
+ | const struct list_head *head) | ||
+ | { | ||
+ | return list->next == head; | ||
+ | } | ||
+ | static inline int list_empty(const struct list_head *head) | ||
+ | { | ||
+ | return head->next == head; | ||
+ | } | ||
+ | static inline struct list_head *list_next(const struct list_head *current) | ||
+ | { | ||
+ | if (list_empty(current)) | ||
+ | return NULL; | ||
+ | else | ||
+ | return current->next; | ||
+ | } | ||
+ | static inline struct list_head *list_prev(const struct list_head *current) | ||
+ | { | ||
+ | if (list_empty(current)) | ||
+ | return NULL; | ||
+ | else | ||
+ | return current->prev; | ||
+ | } | ||
+ | |||
+ | #define list_for_each(pos, head) \ | ||
+ | for (pos = (head)->next; pos != (head); pos = pos->next) | ||
+ | |||
+ | #define list_for_each_prev(pos, head) \ | ||
+ | for (pos = (head)->prev; pos != (head); pos = pos->prev) | ||
+ | |||
+ | //macro for inserting a for that cycle all ITEM of the list | ||
+ | #define list_for_each_entry(pos, head, member) \ | ||
+ | for (pos = container_of((head)->next, typeof(*pos), member); \ | ||
+ | &pos->member != (head); \ | ||
+ | pos = container_of(pos->member.next, typeof(*pos), member)) | ||
+ | |||
+ | #define list_for_each_entry_reverse(pos, head, member) \ | ||
+ | for (pos = container_of((head)->prev, typeof(*pos), member); \ | ||
+ | &pos->member != (head); \ | ||
+ | pos = container_of(pos->member.prev, typeof(*pos), member)) | ||
+ | |||
+ | #endif | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | test program source code: | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include "listx.h" | ||
+ | #include "stdlib.h" | ||
+ | #include "stdio.h" | ||
+ | |||
+ | // Elemento della lista | ||
+ | typedef struct list_item { | ||
+ | int item; | ||
+ | struct list_head list; | ||
+ | } list_item_t; | ||
+ | |||
+ | |||
+ | |||
+ | int main() { | ||
+ | |||
+ | list_item_t* elem1; | ||
+ | list_item_t* elem2; | ||
+ | |||
+ | // Allocazione di memoria per 2 elementi di tipo list_item_t | ||
+ | elem1=(list_item_t*) malloc(sizeof(list_item_t)); | ||
+ | elem2=(list_item_t*) malloc(sizeof(list_item_t)); | ||
+ | |||
+ | elem1->item=3; | ||
+ | elem2->item=5; | ||
+ | |||
+ | // Elemento sentinella | ||
+ | struct list_head head; | ||
+ | |||
+ | //Inizializzazione dell'elemento sentinella | ||
+ | INIT_LIST_HEAD(&head); | ||
+ | |||
+ | // Test di lista vuota | ||
+ | printf("Lista vuota? %s \n",list_empty(&head)?"sì":"no"); | ||
+ | |||
+ | // Aggiunta di due elementi alla lista | ||
+ | list_add(&(elem1->list),&head); | ||
+ | list_add(&(elem2->list),&head); | ||
+ | |||
+ | // Test di lista vuota | ||
+ | printf("Lista vuota? %s \n",list_empty(&head)?"sì":"no"); | ||
+ | |||
+ | // Scorrimento di Lista: Metodo 1 | ||
+ | list_item_t* pos; | ||
+ | list_for_each_entry(pos,&head,list) { | ||
+ | printf("Elemento corrente: %d \n",pos->item); | ||
+ | } | ||
+ | |||
+ | // Scorrimento di Lista: Metodo 2 | ||
+ | struct list_head* pos2; | ||
+ | list_for_each(pos2,&head) { | ||
+ | list_item_t* elem=container_of(pos2,list_item_t,list); | ||
+ | printf("Elemento corrente: %d \n",elem->item); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | return 1; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This list structure is declared to support multiple types. On listx.h only the pointer part is declared. It also use macro routine for creating "generic" code. The preprocessor transform the macro routine in real code depending one the data types used for the list. | ||
+ | |||
+ | == Print child exit status == | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/wait.h> | ||
+ | |||
+ | int main(int argc,char* argv[]) { | ||
+ | int status; | ||
+ | //create a new child process | ||
+ | switch (fork()) { | ||
+ | case 0: //child | ||
+ | //close the process and return as status the number given by argument | ||
+ | exit(atoi(argv[1])); | ||
+ | default: //parent | ||
+ | break; | ||
+ | case -1: //error case | ||
+ | exit(1); | ||
+ | } | ||
+ | //wait for a child status change (like termination) and write it to a variable. | ||
+ | wait(&status); | ||
+ | printf("%d\n",WEXITSTATUS(status)); | ||
+ | printf("FINE\n"); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Demonstration of the use of pipe system call == | ||
+ | This program mimic the effect of "ls | sort -r" command. | ||
+ | <syntaxhighlight lang=C> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/wait.h> | ||
+ | |||
+ | int main(int argc,char* argv[]) { | ||
+ | int pfd[2],status; | ||
+ | //create a pipe pfd[1]==>pfd[0] | ||
+ | pipe(pfd); | ||
+ | switch (fork()) { | ||
+ | case 0: //child process | ||
+ | //set pfd[1] as standard output, close standard output | ||
+ | dup2(pfd[1],STDOUT_FILENO); | ||
+ | //close child process pfd | ||
+ | close(pfd[1]); | ||
+ | close(pfd[0]); | ||
+ | //tranform child process to ls | ||
+ | execlp("ls","ls",0); | ||
+ | exit(2); | ||
+ | default: | ||
+ | break; | ||
+ | case -1: | ||
+ | exit(1); | ||
+ | } | ||
+ | switch (fork()) { | ||
+ | case 0: //child process | ||
+ | //set pfd[1] as standard input, close standard input | ||
+ | dup2(pfd[0],STDIN_FILENO); | ||
+ | //close child process pfd | ||
+ | close(pfd[1]); | ||
+ | close(pfd[0]); | ||
+ | //tranform child process to sort with reverse option | ||
+ | execlp("sort","sort","-r",0); | ||
+ | exit(2); | ||
+ | default: | ||
+ | break; | ||
+ | case -1: | ||
+ | exit(1); | ||
+ | } | ||
+ | close(pfd[1]); | ||
+ | close(pfd[0]); | ||
+ | wait(&status); | ||
+ | } | ||
+ | |||
</syntaxhighlight> | </syntaxhighlight> |
Latest revision as of 17:06, 13 November 2016
C language - No Libraries
The following programs do not use library functions (only printf, just to have a feedback of the results).
arrays, pointers and structs
#include <stdio.h>
char *spoint="hello";
char sarr[]="hello";
struct strs {
char s[6];
} sstruct = {"hello"};
void foo(char *s) {
s[4]=0;
}
void bar(struct strs s) {
s.s[4]=0;
printf("from bar %s\n", s.s);
}
int main(int argc, char *argv[]) {
printf("%s %s %s\n", spoint, sarr, sstruct.s);
foo(sarr);
printf("%s %s %s\n", spoint, sarr, sstruct.s);
bar(sstruct);
printf("%s %s %s\n", spoint, sarr, sstruct.s);
// test the following statements, one at a time
//spoint = sarr;
//sarr = spoint;
foo(spoint);
printf("%s %s %s\n", spoint, sarr, sstruct.s);
}
iteration and recursion
#include <stdio.h>
struct elem {
int val;
struct elem *next;
};
struct elem *head = NULL;
struct elem *rinsert(struct elem *new, struct elem *head) {
if (head == NULL || new->val < head->val) {
new->next = head;
return new;
} else {
head->next = rinsert(new, head->next);
return head;
}
}
struct elem *iinsert(struct elem *new, struct elem *head) {
struct elem **pnext;
for (pnext = &head;
*pnext != NULL && new->val > (*pnext)->val;
pnext = &((*pnext)->next))
;
new->next = *pnext;
*pnext = new;
return head;
}
void rprint(struct elem *this) {
if (this) {
printf("%d ",this->val);
rprint(this->next);
}
}
void iprint(struct elem *this) {
for ( ; this != NULL; this = this->next)
printf("%d ",this->val);
}
struct elem test[]={{5},{3},{9},{1},{7}};
#define NELEM (sizeof(test) / sizeof(struct elem))
int main(int argc, char *argv[]) {
int i;
for (i = 0; i < NELEM; i++)
head = rinsert(&test[i], head);
rprint(head);
printf("\n");
iprint(head);
printf("\n");
head = NULL;
for (i = 0; i < NELEM; i++)
head = iinsert(&test[i], head);
rprint(head);
printf("\n");
iprint(head);
printf("\n");
}
comma operator
#include <stdio.h>
int slen(char *s) { /* in real programs use strlen instead */
size_t rval;
for (rval = 0; *s != 0; s++, rval++)
;
return rval;
}
int ispal(char *s) {
int i,j;
for (i=0, j=slen(s)-1; i < j; i++, j--) {
if (s[i] != s[j])
return 0;
}
return 1;
}
void reverse(char *s) {
int i,j;
for (i=0, j=slen(s)-1; i < j; i++, j--)
s[j] ^= s[i] ^= s[j] ^= s[i];
}
int main(int argc, char *argv[1]) {
for ( ;argc > 1; argv++, argc--) {
printf("\"%s\" is%s palindrome\n", argv[1],
ispal(argv[1])?"":"n't");
reverse(argv[1]);
printf("\"%s\"\n", argv[1]);
}
}
string by value
#include <stdio.h>
int slen(char *s) { /* in real programs use strlen instead */
int rval;
for (rval = 0; *s != 0; s++, rval++)
;
return rval;
}
void printxvowel(char v, char *s) {
int s_len = slen(s);
char locals[s_len];
int i;
for (i=0; i<s_len; i++) {
switch (s[i]) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
locals[i] = v;
break;
default:
locals[i] = s[i];
break;
}
}
printf("-> %s\n<- %s\n",s,locals);
}
int main(int argc, char *argv[]) {
for ( ; argc > 1; argc--, argv++) {
printxvowel('a',argv[1]);
printxvowel('e',argv[1]);
printxvowel('i',argv[1]);
printxvowel('o',argv[1]);
printxvowel('u',argv[1]);
}
return 0;
}
test it using "Garibaldi fu ferito, fu ferito ad una gamba, Garibaldi che comanda, che comanda il battaglion" as argv[1].
tables and preprocessor tricks
#include <stdio.h>
#define rows_of(X) (sizeof(X) / sizeof((X)[0]))
#define printTable(X) do { \
int i; \
printf("TABLE " #X ": size of element %d\n" \
"(printed by the line %d of source file %s)\n", \
sizeof(*(table ## X)), __LINE__, __FILE__); \
for (i = 0; i < rows_of(table ## X); i++) \
printf(#X " %02d %s\n",i,table ## X [i]); \
} while (0);
char tableA[][50] = {"Sempre caro mi fu quest'ermo colle,",
"e questa siepe, che da tanta parte",
"dell’ultimo orizzonte il guardo esclude."};
char *tableB[] = {"Sempre caro mi fu quest'ermo colle,",
"e questa siepe, che da tanta parte",
"dell’ultimo orizzonte il guardo esclude."};
int main(int argc, char *argv[1]) {
int i;
printTable(A);
printf("\n");
printTable(B);
}
arrays and pointers
#include <stdio.h>
int slen(char *s) { /* in real programs use strlen instead */
size_t rval;
for (rval = 0; *s != 0; s++, rval++)
;
return rval;
}
void echoargs(int argc, char *argv[]) {
int i;
for (i = 0; i < argc; i++)
printf("argv[%d] = \"%s\"\n",i,argv[i]);
printf("\n");
}
enum state {SPACE, CHAR};
int splitargv(char *s, char **argv) {
enum state state = SPACE;
int count = 0;
//until the string is finished
for (; *s != 0; s++) {
//if s point to a character that is a space or a tab or a new line
if (*s == ' ' || *s == '\t' || *s == '\n') {
if (state != SPACE)
count++;
if (argv != NULL)
//insert a zero for ending the string
*s = 0;
state = SPACE;
} else {
if (state == SPACE && argv != NULL)
//if it is in SPACE state but the pointed character is not a "space" it splits the string by setting that the next element of the argv array should point to the new string.
*argv++ = s;
state = CHAR;
}
};
if (state != SPACE)
count++;
if (argv != NULL)
*argv = NULL;
return count;
}
void splitargs(char *args) {
int newargc = splitargv(args, NULL);
char *newargv[newargc + 1];
splitargv(args, newargv);
echoargs(newargc, newargv);
}
int main(int argc, char *argv[1]) {
echoargs(argc, argv);
for ( ; *argv != NULL; argv++) {
printf("Split \"%s\"\n",*argv);
splitargs(*argv);
}
}
This is a simplified version of the idea used in the libexecs library. Test this program using args like:
./a.out "ciao mare" "a b c"
This program split the strings contained in the argv array by truncating them on space, tab and new line characters.
The new strings are inserted in the argv array and argc is increased properly. FedericoB (talk)
void * and function pointers
#include <stdio.h>
typedef void (*voidfun) (void *arg);
void printint(void *arg) {
int *iarg = arg;
printf("int %d\n", *iarg);
}
void printstring(void *arg) {
char *sarg = arg;
printf("int %s\n", sarg);
}
void printpointer(void *arg) {
printf("pointer %p\n", arg);
}
void printfun(void *arg) {
voidfun fun = arg;
fun((void *) 0x42);
}
void launch(voidfun f, void *opaque) {
f(opaque);
}
int main(int argc, char *argv[1]) {
int v = 235;
char *s = "Lasciate ogni speranza, o voi ch'entrate";
launch(printint, &v);
launch(printstring, s);
launch(printfun, printpointer);
return 0;
}
This technique is used to implement callbacks with opaque args.
Programs using libraries
char by char copy (stdio)
#include <stdio.h>
int main(int argc, char *argv[]) {
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
similar programs, different approaches (stdio, string, stdlib, ctype)
First program:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define BUFLEN 4096
char *loadfile(char *path) {
char *contents = NULL;
size_t contents_len = 0;
char buffer[BUFLEN];
size_t len;
FILE *fin = fopen(path, "r");
FILE *fout = open_memstream(&contents, &contents_len);
if (fin == NULL || fout == NULL)
exit(1);
while ((len = fread(buffer, sizeof(char), BUFLEN, fin)) > 0)
fwrite(buffer, sizeof(char), len, fout);
fclose(fin);
fclose(fout);
return(contents);
}
enum state {SPACE, CHAR};
int splitwords(char *s, char **words) {
enum state state = SPACE;
int count = 0;
for (; *s != 0; s++) {
if (isalnum(*s)) {
if (state == SPACE && words != NULL)
*words++ = s;
state = CHAR;
} else {
if (state != SPACE)
count++;
if (words != NULL)
*s = 0;
state = SPACE;
}
};
if (state != SPACE)
count++;
return count;
}
static int sortcmp(const void *p1, const void *p2) {
const char *s1 = *(char * const *) p1;
const char *s2 = *(char * const *) p2;
int rval = strlen(s1) - strlen(s2);
if (rval == 0)
rval = strcmp(s1, s2);
return rval;
}
int main(int argc, char *argv[1]) {
if (argc == 2) {
char *contents = loadfile(argv[1]);
int count = splitwords(contents, NULL);
char **words = malloc(count * sizeof(char *));
if (words == NULL)
return 1;
else {
int i;
splitwords(contents, words);
qsort(words, count, sizeof(char *), sortcmp);
for (i = 0; i < count; i++)
printf("%s\n", words[i]);
return 0;
}
} else
return 1;
}
Second program:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
struct word {
struct word *next;
int count;
char word[];
};
static int lenalphacmp(const char *s1, const char *s2) {
int rval = strlen(s1) - strlen(s2);
if (rval == 0)
rval = strcmp(s1, s2);
return rval;
}
struct word *addword(char *word, struct word *wlist) {
struct word **scan;
int cmpvalue;
for (scan = &wlist;
*scan != NULL && ((cmpvalue = lenalphacmp(word, (*scan)->word)) > 0);
scan = &((*scan)->next))
;
if (scan != NULL && cmpvalue == 0)
(*scan)->count ++;
else {
struct word *new = malloc(sizeof(struct word) + strlen(word) + 1);
if (new) {
new->next = *scan;
new->count = 1;
strcpy(new->word, word);
*scan = new;
}
}
return wlist;
}
struct word *addwordlen(char *word, size_t len, struct word *wlist) {
char word0[len+1];
strncpy(word0,word,len);
word0[len]=0;
return addword(word0, wlist);
}
struct word *processline(char *line, struct word *wlist) {
char *thisword = NULL;
for ( ;*line != 0; line++) {
if (isalnum(*line)) {
if (thisword == NULL)
thisword = line;
} else {
if (thisword != NULL) {
wlist = addwordlen(thisword, line - thisword, wlist);
thisword = NULL;
}
}
}
return wlist;
}
void printwlist(struct word *wlist) {
for ( ; wlist != NULL; wlist = wlist->next)
printf("%s : %d\n", wlist->word, wlist->count);
}
int main(int argc, char *argv[1]) {
if (argc == 2) {
FILE *fin = fopen(argv[1], "r");
ssize_t len;
char *linebuf = NULL;
size_t linebuflen = 0;
struct word *wlist = NULL;
while ((len = getline(&linebuf, &linebuflen, fin)) > 0)
wlist = processline(linebuf, wlist);
fclose(fin);
free(linebuf);
printwlist(wlist);
return 0;
} else
return 1;
}
Command line args management (getopt)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
void usage_and_exit(char *progname) {
fprintf(stderr,
"Usage:\n"
" %s OPTIONS [value] ... [value]\n"
"Options:\n"
" -v | --verbose: verbose mode\n"
" -h | --help: print a short usage helping message\n"
" -a val | --add val add a value\n"
" -s val | --sub val subtract a value\n"
" --double double the final result\n"
"This program computes an integer value\n", progname);
exit(1);
}
int main(int argc, char *argv[]) {
static struct option long_options[] = {
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"add", required_argument, 0, 'a'},
{"sub", required_argument, 0, 's'},
{"double", no_argument, 0, 0x101},
};
static char *short_options = "vha:s:";
int opt;
int value = 0;
int verbose = 0;
int doubleval = 1;
char *progname = basename(argv[0]);
while((opt = getopt_long(argc, argv, short_options, long_options, NULL)) > 0) {
switch (opt) {
case 'v': verbose = 1;
break;
case 'a': value += atoi(optarg);
break;
case 's': value -= atoi(optarg);
break;
case 0x101: doubleval *= 2;
break;
case 'h':
case '?': usage_and_exit(progname);
break;
}
}
for (; optind < argc; optind++)
value += atoi(argv[optind]);
value *= doubleval;
if (verbose)
printf("the result is %d\n", value);
return value;
}
This program uses the getopt function for parsing the arguments. Starting from a base value of 0, it gives the possibility to add a number, subtract it and multiply it by two. The final value is the return code of the main or printed if verbose mode is activated.
dynamic allocation on the heap, qsort, getline (stdlib)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define AOS_LENSTEP 8
/*
* Define a struct for managing and array of pointer to strings.
*/
struct array_of_strings {
char **strings;
size_t currentlength; //the length of number of string in memory
size_t arraylength; //the length of memory allocated
};
typedef struct array_of_strings array_of_strings;
void array_of_strings_add(array_of_strings *arrayOfStrings, char *string) {
//if there is not enough space for the string increase it
if (arrayOfStrings->currentlength >= arrayOfStrings->arraylength) {
//increase the array length by the size of a string pointer
size_t newlength = arrayOfStrings->arraylength + AOS_LENSTEP;
//reallocate the arrayOfString with the new size
char **new_string = realloc(arrayOfStrings->strings, newlength * sizeof(arrayOfStrings->strings[0]));
//if the reallocation is successful
if (new_string != NULL) {
arrayOfStrings->arraylength = newlength;
arrayOfStrings->strings = new_string;
}
}
//if there is enough space for the string insert it
if (arrayOfStrings->currentlength < arrayOfStrings->arraylength)
//strdup return a pointer to a duplicate of the string
arrayOfStrings->strings[arrayOfStrings->currentlength++] = strdup(string);
}
void array_of_strings_print(array_of_strings *v) {
size_t i;
for (i = 0; i < v->currentlength; i++)
printf("[%3lu]: %s\n", i, v->strings[i]);
}
static int alphasort(const void *a, const void *b) {
return strcmp(*(char **) a, *(char **) b);
}
void array_of_strings_sort(array_of_strings *v) {
qsort(v->strings, v->currentlength, sizeof(v->strings[0]), alphasort);
}
void array_of_strings_free(array_of_strings *v) {
size_t i;
for (i = 0; i < v->currentlength; i++)
free(v->strings[i]);
if (v->arraylength > 0)
free(v->strings);
}
int main(int argc, char *argv[]) {
char* line = NULL;
size_t lineLength = 0;
//size is signed but can store values [-1, SSIZE_MAX]
ssize_t numberOfCharactersRead;
static array_of_strings arrayOfStrings;
if (argc != 1)
exit(1);
//stdin is the standard input from terminal
while ((numberOfCharactersRead = getline(&line, &lineLength, stdin)) >= 0) {
if (line[numberOfCharactersRead - 1] == '\n')
line[numberOfCharactersRead - 1] = 0;
array_of_strings_add(&arrayOfStrings, line);
}
free(line);
array_of_strings_sort(&arrayOfStrings);
array_of_strings_print(&arrayOfStrings);
array_of_strings_free(&arrayOfStrings);
}
This program demonstrates the use of getline for reading an input stream, realloc for dynamic memory allocation and qsort for sorting an array of dynamic allocated array of strings.
For executing this program is suggest to use input redirection by typing "program < inputfile". In this way the while loop will be stopped by reaching end of file.
open_memstream (quite tricky)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int alphasort(const void *a, const void *b) {
return strcmp(* (char **) a, * (char **) b);
}
int main(int argc, char *argv[]) {
char *line = NULL;
size_t lineLength = 0;
ssize_t numberOfCharacterRead;
FILE *fakefile;
char *fakeMemoryFile = NULL;
char **stringsarray;
size_t stringsArrayLength = 0;
char **scan;
char *dups;
if (argc != 1)
exit(1);
fakefile = open_memstream(&fakeMemoryFile, &stringsArrayLength);
if (fakefile == NULL)
exit(1);
while ((numberOfCharacterRead = getline(&line, &lineLength, stdin)) >= 0) {
if (line[numberOfCharacterRead - 1] == '\n')
line[numberOfCharacterRead - 1] = 0;
if ((dups = strdup(line)) != NULL)
//write the duplicated read line to the memory stream
fwrite(&dups, sizeof(dups), 1, fakefile);
}
free(line);
dups = NULL;
//write a null string at the end of the stream
fwrite(&dups, sizeof(dups), 1, fakefile);
fclose(fakefile);
stringsarray = (char **) fakeMemoryFile;
//obtain the number of strings contained in the array
stringsArrayLength /= sizeof(char *);
//ignore the ending null string
stringsArrayLength--;
printf("%lu\n",stringsArrayLength);
qsort(stringsarray, stringsArrayLength, sizeof(stringsarray[0]), alphasort);
for (scan = stringsarray; *scan != NULL; scan++)
printf("[%3lu]: %s\n", scan - stringsarray, *scan);
for (scan = stringsarray; *scan != NULL; scan++)
free(*scan);
free(stringsarray);
}
This program is similar to 2.4. But instead of using an array structure it use a file stored in memory that isn't stored in the hard drive.
fantasy of strcpy (time, stdint, string)
Cit. Cirano page 33.
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
char *strcpy0(char *dest, const char *src) {
int i;
int len;
//determine the length of src
for (len = 0; src[len] != 0; len++)
;
//copy src into dest
for (i = 0; i <= len; i++)
dest[i] = src[i];
return dest;
}
char *strcpy1(char *dest, const char *src) {
int i;
//set i as the lenth of src
for (i = 0; src[i] != 0; i++)
;
//backward copy src into dest
for ( ; i >= 0; i--)
dest[i] = src[i];
return dest;
}
char *strcpy2(char *dest, const char *src) {
char *rval = dest;
//copy the characters until the end of the string
while ((*dest++ = *src++) != 0)
;
return rval;
}
/**
* Same as strcpy2 but with array format instead of pointer. Notice that this introduce an overhead due to calculating offset.
*/
char *strcpy3(char *dest, const char *src) {
int i;
for (i=0; (dest[i] = src[i]) != 0; i++)
;
return dest;
}
/**
* Recursive copy of the string.
*/
char *strcpy4(char *dest, const char *src) {
if ((*dest = *src) != 0)
strcpy4(dest+1, src+1);
return dest;
}
/**
* Similar to strcpy2
*/
char *strcpy5(char *dest, const char *src) {
char *rval = dest;
while (*src != 0)
*dest++ = *src++;
*dest = 0;
return rval;
}
/**
* This strcpy copy 64 bit at a time instead of 8 bit.
*/
char *strcpy6(char *dest, const char *src) {
//unsigned long long pointer
uint64_t *src64 = (uint64_t *) src;
uint64_t *dest64 = (uint64_t *) dest;
while (1) {
//says to the compiler to keep tmp in a register
register uint64_t tmp = *src64;
register uint64_t scan;
//detect if the current tmp cointans a 0 character
for (scan = 0xffULL; (tmp & scan) != 0; scan <<= 8)
;
//printf("%llx %llx %p %p %s\n",scan, tmp, src64, dest64, (char *) src64);
//if the current tmp cointains a 0 character uses strcpy2 for wiser copy.
if (scan != 0) {
strcpy2((char *) dest64, (char *) src64);
break;
}
*dest64++ = tmp;
src64++;
}
return dest;
}
#define NUMBER_OF_TEST 1000000
double time_strcpy(char * (*cpy)(char *, const char *), char *d, const char *s)
{
clock_t starttime, time;
int i;
starttime = clock();
for (i = 0; i< NUMBER_OF_TEST; i++)
cpy(d, s);
time = clock() - starttime;
return (1.0) * time / CLOCKS_PER_SEC;
}
/**
* Calls the n-th version of the strcopy and calculates the execution of it. Prints the result.
*/
#define TESTSTRCPY(N,D,S) \
printf("strcpy" #N " %s time %lf\n", strcpy ##N ((D),(S)), time_strcpy(strcpy ##N, (D),(S)))
int main(int argc, char *argv[]) {
if (argc != 2)
return 1;
else {
size_t argv1len=strlen(argv[1]);
char argcpy[argv1len+1];
strncpy(argcpy, argv[1], argv1len);
printf("strncpy %s\n", argcpy);
TESTSTRCPY(, argcpy, argv[1]);
TESTSTRCPY(0, argcpy, argv[1]);
TESTSTRCPY(1, argcpy, argv[1]);
TESTSTRCPY(2, argcpy, argv[1]);
TESTSTRCPY(3, argcpy, argv[1]);
TESTSTRCPY(4, argcpy, argv[1]);
TESTSTRCPY(5, argcpy, argv[1]);
TESTSTRCPY(6, argcpy, argv[1]);
}
}
This program compares various homemade implementation of string copy function with the library one.
math and printf
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#define DELAY 10000
#define DURATION 30
int main(int argc, char *argv[]) {
double x;
int i;
for (i = 0; i < DURATION * 1000000 / DELAY; i++) {
double y = sin(M_PI * i / 100);
int pos = 40 + (40 * y);
//\r is carriage return \b move the character back one position.
printf("\r%*s\b",pos,"*");
//print the remaing character of stream and clean it
fflush(stdout);
//wait a DELAY time
usleep(DELAY);
printf("\n");
}
}
This program print a vertical sine curve on the terminal. It uses the math library for calculating the sine value and unistd for making the animation(with the use of usleep function).
minishell (history and execs)
... questo esercizio richiede il caricamento di librerie da installare (history/readline da distribuzione, execs da github).
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <execs.h>
int main(int argc, char *argv[]) {
while (1) {
//prompt the given string and read a line of text
char *cmd = readline("% ");
if (cmd == NULL)
break;
if (*cmd != 0) {
//add the string to the end of shell history
add_history(cmd);
//execute the command without starting a shell
system_nosh(cmd);
}
free(cmd);
}
printf("\n");
return 0;
}
Per il linking dinamico della libreria execs consiglio di impostare la variabile d'ambiente LD_LIBRARY_PATH al giusto percorso tramite i comandi:
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
Questo programma legge una riga di testo e la esegue come comando.
conversions (math)
#include <stdio.h>
#include <math.h>
int main() {
char c;
short s;
int i;
long l;
long long ll;
unsigned char uc;
unsigned short us;
unsigned int ui;
unsigned long ul;
unsigned long long ull;
float f;
double d;
long double ld;
c = s = i = l = ll = 0xc1c2c3c4c5c6c7c8;
printf("%d %llx %lld\n", sizeof(ll), ll, ll);
printf("%d %lx %ld\n", sizeof(l), l, l);
printf("%d %x %d\n", sizeof(i), i, i);
printf("%d %x %d\n", sizeof(s), s, s);
printf("%d %x %d\n", sizeof(c), c, c);
ll = l = i = s = c;
printf("%d %x %d\n", sizeof(s), s, s);
printf("%d %x %d\n", sizeof(i), i, i);
printf("%d %lx %ld\n", sizeof(l), l, l);
printf("%d %llx %lld\n", sizeof(ll), ll, ll);
//set on i a 1 on the last left position
i = 1 << (sizeof(i) * 8 - 1);
printf("%d %x %d\n", sizeof(i), i, i);
i >>= 1;
printf("%d %x %d\n", sizeof(i), i, i);
i = -1;
printf("%d %x %d\n", sizeof(i), i, i);
i >>= 1;
printf("%d %x %d\n", sizeof(i), i, i);
uc = us = ui = ul = ull = 0xc1c2c3c4c5c6c7c8;
printf("%d %llx %llu\n", sizeof(ull), ull, ull);
printf("%d %lx %lu\n", sizeof(ul), ul, ul);
printf("%d %x %u\n", sizeof(ui), ui, ui);
printf("%d %x %u\n", sizeof(us), us, us);
printf("%d %x %u\n", sizeof(uc), uc, uc);
ull = ul = ui = us = uc;
printf("%d %x %u\n", sizeof(us), us, us);
printf("%d %x %u\n", sizeof(ui), ui, ui);
printf("%d %lx %lu\n", sizeof(ul), ul, ul);
printf("%d %llx %llu\n", sizeof(ull), ull, ull);
f = d = ld = acosl(-1);
printf("%2d %80.78llf\n", sizeof(ld), ld);
printf("%2d %80.78lf\n", sizeof(d), d);
printf("%2d %80.78f\n", sizeof(f), f);
return 0;
}
Explanation of program's output for a LP64 architecture:
//size-hexadecimal-decimal
8 c1c2c3c4c5c6c7c8 -4484807029008447544 //long long is equal to long in LP64
8 c1c2c3c4c5c6c7c8 -4484807029008447544
4 c5c6c7c8 -976828472 //first 4 byte truncated
2 ffffc7c8 -14392 //the number is saved in two's complement so negative numbers have some 1 to the left.
1 ffffffc8 -56
2 ffffffc8 -56
4 ffffffc8 -56
8 ffffffffffffffc8 -56
8 ffffffffffffffc8 -56
4 80000000 -2147483648
4 c0000000 -1073741824 //the left-most byte becomes 11000000 instead of 01000000 because the number was negative.
4 ffffffff -1 //in two's complement the number with all 1 represents -1
4 ffffffff -1 //-1 is negative so right shifting it add a 1 to the left.
8 c1c2c3c4c5c6c7c8 13961937044701104072 //unsigned type can hold higher values.
8 c1c2c3c4c5c6c7c8 13961937044701104072
4 c5c6c7c8 3318138824
2 c7c8 51144 //this number is unsigned so showing it on two's complement doesn't include the 1 to the left.
1 c8 200
2 c8 200
4 c8 200
8 c8 200
8 c8 200
//various floating-point notations
16 3.141592653589793238512808959406186204432742670178413391113281250000000000000000 //long double
8 3.141592653589793115997963468544185161590576171875000000000000000000000000000000 //double
4 3.141592741012573242187500000000000000000000000000000000000000000000000000000000 //float
A home-brewed mini-printf (variadic functions, indirect recursion)
#include <stdio.h>
#include <stdarg.h>
int vrprintf(const char *format, va_list ap);
//static function are functions that are only visible to functions in this file
//writes the given character (as int) to the standard output it the character is not 0
static int putcx(int c) {
if (c!=0) {
putchar(c);
return 1;
} else
return 0;
}
static char backchar[128] = {
[0 ... 127] = 0,
['a'] = '\a',
['b'] = '\b',
['f'] = '\f',
['n'] = '\n',
['r'] = '\r',
['t'] = '\t',
['v'] = '\v',
['\\'] = '\\', // double escape character is needed for saving the escape character itself in a variable
['\''] = '\'', //use the escape character for saving the apostrophe
['"'] = '"',
['?'] = '?',
};
static int put_backslash(char escapeChar) {
//write the backslash
//the and-bitwise is for switch off the last bit (ASCII compatibility)
return putcx(backchar[escapeChar & 0x7f]);
}
static int rvrp_int(int val) {
if (val == 0)
return 0;
else //recurvise call on the more significant digit and print the less significant
return rvrp_int(val / 10) + putcx('0' + val % 10);
}
static int vrp_int(int val) {
if (val != 0) {
if (val < 0)
return putcx('-') + rvrp_int(-val);
else
return rvrp_int(val);
} else
return putcx('0');
}
static int vrp_string(char *s) {
switch (*s) {
case 0:
return 0;
case '\\':
//if after a \ the string not ends recursive call on next character otherwise return 0
return put_backslash(*(s+1)) ? vrp_string(s + 2) + 1 : 0;
default:
return putcx(*s) + vrp_string(s + 1);
}
}
static int vrp_percent(const char *format, va_list ap) {
switch (*format) {
case 0:
return 0;
case '%':
//case when a % is followed by a % so print a % and advance
return putcx(*format) + vrprintf(format + 1, ap);
case 'd':
//call vrp_int with the next argument of the list with type int
return vrp_int(va_arg(ap, int)) + vrprintf(format + 1, ap);
case 's':
//call vrp_int with the next argument of the list with type pointer to char
return vrp_string(va_arg(ap, char *)) + vrprintf(format + 1, ap);;
default:
//if the format is not recognized
printf("ERROR\n");
return 0;
}
}
int vrprintf(const char *format, va_list ap) {
//parse the format
switch (*format) {
//case format null
case 0:
return 0;
//case format %: call vrp_percent and advance 1 position
case '%':
return vrp_percent(format + 1, ap);
case '\\':
//if after a \ the string not ends recursive call on next character otherwise return 0
return put_backslash(*(format+1)) ? vrprintf(format + 2, ap) + 1 : 0;
default:
//print the character and recursive call on next character
return putcx(*format) + vrprintf(format + 1, ap);
}
}
//function with undetermined number of arguments. Returns the number of characters printed.
int rprintf(const char *format, ...) {
int rval;
//declare a list of arguments
va_list ap;
//initialize the list
va_start (ap, format);
rval = vrprintf(format, ap);
//clean up the list
va_end(ap);
return rval;
}
int main() {
int v;
v = rprintf("hello world\n");
printf("%d\n", v);
v = printf("hello world\n");
printf("%d\n", v);
v = rprintf("hello world %d\n", 10);
printf("%d\n", v);
v = printf("hello world %d\n", 10);
printf("%d\n", v);
v = rprintf("hello world %s %d\n", "piripicchio", 42);
printf("%d\n", v);
v = printf("hello world %s %d\n", "piripicchio", 42);
printf("%d\n", v);
v = rprintf("hello world %% \"%s\" %d\n", "piripicchio\tbackslash", 42);
printf("%d\n", v);
v = printf("hello world %% \"%s\" %d\n", "piripicchio\tbackslash", 42);
printf("%d\n", v);
v = rprintf("%%\n");
printf("%d\n", v);
v = printf("%%\n");
printf("%d\n", v);
}
The program shows the use of indirect recursion (for example in rvrp_int e vvrp_int function) and variadic functions for emulate the printf.
Linux kernel (alike) lists
File listx.h:
/* subset of the Linux Kernel source file: "include/linux/list.h"
CPLv2 */
#ifndef _LISTX_H
#define _LISTX_H
#define NULL 0
typedef unsigned int size_tt;
//macro for obtaining a pointer to the struct given a member of it
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
//macro for obtaining the offset of a struct's member.
#define offsetof(TYPE, MEMBER) ((size_tt) &((TYPE *)0)->MEMBER)
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
//when you declare a function 'inline' gcc integrate that function'c code into the code of its callers.
static inline void INIT_LIST_HEAD(struct list_head *list)
{
//circular list, so when it's empty next and previus point to list
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
static inline struct list_head *list_next(const struct list_head *current)
{
if (list_empty(current))
return NULL;
else
return current->next;
}
static inline struct list_head *list_prev(const struct list_head *current)
{
if (list_empty(current))
return NULL;
else
return current->prev;
}
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
//macro for inserting a for that cycle all ITEM of the list
#define list_for_each_entry(pos, head, member) \
for (pos = container_of((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = container_of(pos->member.next, typeof(*pos), member))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = container_of((head)->prev, typeof(*pos), member); \
&pos->member != (head); \
pos = container_of(pos->member.prev, typeof(*pos), member))
#endif
test program source code:
#include "listx.h"
#include "stdlib.h"
#include "stdio.h"
// Elemento della lista
typedef struct list_item {
int item;
struct list_head list;
} list_item_t;
int main() {
list_item_t* elem1;
list_item_t* elem2;
// Allocazione di memoria per 2 elementi di tipo list_item_t
elem1=(list_item_t*) malloc(sizeof(list_item_t));
elem2=(list_item_t*) malloc(sizeof(list_item_t));
elem1->item=3;
elem2->item=5;
// Elemento sentinella
struct list_head head;
//Inizializzazione dell'elemento sentinella
INIT_LIST_HEAD(&head);
// Test di lista vuota
printf("Lista vuota? %s \n",list_empty(&head)?"sì":"no");
// Aggiunta di due elementi alla lista
list_add(&(elem1->list),&head);
list_add(&(elem2->list),&head);
// Test di lista vuota
printf("Lista vuota? %s \n",list_empty(&head)?"sì":"no");
// Scorrimento di Lista: Metodo 1
list_item_t* pos;
list_for_each_entry(pos,&head,list) {
printf("Elemento corrente: %d \n",pos->item);
}
// Scorrimento di Lista: Metodo 2
struct list_head* pos2;
list_for_each(pos2,&head) {
list_item_t* elem=container_of(pos2,list_item_t,list);
printf("Elemento corrente: %d \n",elem->item);
}
return 1;
}
This list structure is declared to support multiple types. On listx.h only the pointer part is declared. It also use macro routine for creating "generic" code. The preprocessor transform the macro routine in real code depending one the data types used for the list.
Print child exit status
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc,char* argv[]) {
int status;
//create a new child process
switch (fork()) {
case 0: //child
//close the process and return as status the number given by argument
exit(atoi(argv[1]));
default: //parent
break;
case -1: //error case
exit(1);
}
//wait for a child status change (like termination) and write it to a variable.
wait(&status);
printf("%d\n",WEXITSTATUS(status));
printf("FINE\n");
}
Demonstration of the use of pipe system call
This program mimic the effect of "ls | sort -r" command.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc,char* argv[]) {
int pfd[2],status;
//create a pipe pfd[1]==>pfd[0]
pipe(pfd);
switch (fork()) {
case 0: //child process
//set pfd[1] as standard output, close standard output
dup2(pfd[1],STDOUT_FILENO);
//close child process pfd
close(pfd[1]);
close(pfd[0]);
//tranform child process to ls
execlp("ls","ls",0);
exit(2);
default:
break;
case -1:
exit(1);
}
switch (fork()) {
case 0: //child process
//set pfd[1] as standard input, close standard input
dup2(pfd[0],STDIN_FILENO);
//close child process pfd
close(pfd[1]);
close(pfd[0]);
//tranform child process to sort with reverse option
execlp("sort","sort","-r",0);
exit(2);
default:
break;
case -1:
exit(1);
}
close(pfd[1]);
close(pfd[0]);
wait(&status);
}