Thursday, June 16, 2011

Utility Functions

1.  <stdlib.h>  Utility Functions

The header <stdlib.h> declares functions for number conversion, storage allocation, and similar tasks.
double strtod(const char *s, char **endp)
strtod converts the prefix of s to double, ignoring leading white space; it stores a pointer to any unconverted suffix in *endp unless endp is NULL. If the answer would overflow, HUGE_VAL (in <math.h>) is returned with the proper sign; if the answer would underflow, zero is returned. In either case errno is set to ERANGE.
double atof(const char *s)
atof converts s to double; it is equivalent to strtod(s, (char**)NULL).
long strtol(const char *s, char **endp, int base)
strtol converts the prefix of s to long, ignoring leading white space; it stores a pointer to any unconverted suffix in *endp unless endp is NULL. If base is between 2 and 36, conversion is done assuming that the input is written in that base. If base is zero, the base is 8, 10, or 16; leading 0 implies octal and leading 0x or 0X hexadecimal. Letters in either case represent digits from 10 to base-1; a leading 0x or 0X is permitted in base 16. If the answer would overflow, LONG_MAX or LONG_MIN (in <limits.h>) is returned, depending on the sign of the result, and errno is set to ERANGE.
unsigned long strtoul(const char *s, char **endp, int base)
strtoul is the same as strtol except that the result is unsigned long and the error value is ULONG_MAX (in <limits.h>).
int atoi(const char *s)
atoi converts s to int; it is equivalent to (int)strtol(s, (char**)NULL, 10).
long atol(const char *s)
atol converts s to long; it is equivalent to strtol(s, (char**)NULL, 10).
int abs(int n)
abs returns the absolute value of its int argument.
long labs(long n)
labs returns the absolute value of its long argument.
div_t div(int num, int denom)
div computes the quotient and remainder of num/denom. The results are stored in the int members quot and rem of a structure of type div_t.
ldiv_t ldiv(long num, long denom)
ldiv computes the quotient and remainder of num/denom. The results are stored in the long members quot and rem of a structure of type ldiv_t.
int rand(void)
rand returns a pseudo-random integer in the range 0 to RAND_MAX, which is at least 32,767.
void srand(unsigned int seed)
srand uses seed as the seed for a new sequence of pseudo-random numbers. The initial seed is 1.
void *malloc(size_t size)
malloc returns a pointer to space for an object of size size, or NULL if the request cannot be satisfied. The space is uninitialized.
void *calloc(size_t nobj, size_t size)
calloc returns a pointer to space for an array of nobj objects, each of size size, or NULL if the request cannot be satisfied. The space is initialized to zero bytes.
void *realloc(void *p, size_t size)
realloc changes the size of the object pointed to by p to size. The contents will be unchanged up to the minimum of the old and new sizes. If the new size is larger, the new space is uninitialized. realloc returns a pointer to the new space, or NULL if the request cannot be satisfied, in which case *p is unchanged.
void free(void *p)
free deallocates the space pointed to by p; it does nothing if p is NULL. p must be a pointer to space previously allocated by calloc, malloc, or realloc.
void qsort(void *base, size_t n, size_t size,
int (*cmp)(const void *e1, const void *e2))
qsort sorts into ascending order an array base[0]...base[n-1] of elements of size size. It calls the comparison function cmp to compare pairs of elements. The comparison function must return negative if e1 is less than e2, zero if equal, and positive if greater. Array elements that are equal may appear in any order.
void *bsearch(const void *key, const void *base, size_t n, size_t size,
                    int (*cmp)(const void *k, const void *e))
bsearch searches an array base[0]...base[n-1] of elements of size size for an element that matches the search key key. It calls the comparison function cmp to compare the search key with elements of the array. The comparison function must return negative if the search key k is less than the array element e, zero if equal, and positive if greater. The array elements must be in ascending order. bsearch returns a pointer to a matching element, or NULL if none exists.
void abort(void)
abort causes the program to terminate abnormally, as if by raise(SIGABRT) (in <signal.h>).
int atexit(void (*fcn)(void))
atexit registers the function fcn to be called when the program terminates normally; it returns non-zero if the registration cannot be made.
void exit(int status)
exit causes normal program termination. atexit functions are called in reverse order of registration, open files are flushed, open streams are closed, and control is returned to the environment. How status is returned to the environment is implementation-dependent, but zero is taken as successful termination. The values EXIT_SUCCESS  (0) and EXIT_FAILURE may also be used.
int system(const char *s)
system passes the string s to the environment for execution. If s is NULL, system returns non-zero if there is a command processor. If s is not NULL, the return value is implementation-dependent.
char *getenv(const char *name)
getenv returns the environment string associated with name, or NULL if no string exists. Details are implementation-dependent.
EXAMPLE CODES
1.       stdlib.h
1.1   strtod ~ srand
l lib-1.c
#include <stdio.h>
#include <stdlib.h>
#include <limits.h> // LONG_MIN, LONG_MAX, ULONG_MAX
#include <errno.h>  // errno
void main() {
    double d; char *p;
    long l; unsigned long ul;

    printf("%g\n", atof(" -012.34E-1"));   // -1.234
    d = strtod("+01234abc", &p);
    printf("%g [%s]\n", d, p);             // 1234 [abc]
    printf("%g\n", strtod("ABC", NULL));   // 0

    printf("%d %ld\n", atoi("+01.9"), atol(" -2A")); // 1 -2
    printf("%d %ld\n", abs(-1), labs(-2L));          // 1 2

    l = strtol(" -12.34abc", &p, 0);
    printf("%ld [%s]\n", l, p);                // -12 [.34abc]
    printf("%ld\n", strtol("-012", NULL,  0)); // -10
    printf("%ld\n", strtol("0x1A", NULL,  0)); // 26
    printf("%ld\n", strtol(" -11", NULL,  2)); // -3
    printf("%ld\n", strtol(" -11", NULL,  5)); // -6
    printf("%ld\n", strtol(" -11", NULL, 10)); // -11
    printf("%ld\n", strtol(" ABC", NULL, 10)); // 0
    
    printf("%ld, %ld, %lu\n", LONG_MIN, LONG_MAX, ULONG_MAX);
    // -2147483648, 2147483647, 4294967295
    l  = strtol ("3000000000abc", &p, 10);
    printf("%ld [%s]\n", l, p);    // 2147483647 [abc]
    perror(NULL);      // Result too large
    errno = 0;
    ul = strtoul("3000000000abc", &p, 10);
    printf("%lu [%s]\n", ul, p);   // 3000000000 [abc]
    perror(NULL);      // No error
}
l lib-2.c
#include <stdio.h>
#include <stdlib.h>
void main() {
    div_t d; ldiv_t ld;

    // sign of the remainder: sign of the numerator
    // (+13) = (+4)*(+3) + (+1),  (+13) = (-4)*(-3) + (+1)
    // (-13) = (+4)*(-3) + (-1),  (-13) = (-4)*(+3) + (-1)

    printf("%+d %+d\n", +13/+4, +13%+4);   // +3 +1
    printf("%+d %+d\n", +13/-4, +13%-4);   // -3 +1
    printf("%+d %+d\n", -13/+4, -13%+4);   // -3 -1
    printf("%+d %+d\n", -13/-4, -13%-4);   // +3 -1

    d = div(+13, +4); printf("%+d %+d\n", d.quot, d.rem);  // +3 +1
    d = div(+13, -4); printf("%+d %+d\n", d.quot, d.rem);  // -3 +1
    d = div(-13, +4); printf("%+d %+d\n", d.quot, d.rem);  // -3 -1
    d = div(-13, -4); printf("%+d %+d\n", d.quot, d.rem);  // +3 -1

    ld = ldiv(13L, 4L); printf("%+ld %+ld\n", ld.quot, ld.rem);    // +3 +1
}
l rand-1.c
#include <stdio.h>
#include <stdlib.h>
void main() {
    int i;
    for (i = 0; i < 5; i++) printf("%d ", rand());
    putchar('\n');
}
>   rand-1
41 18467 6334 26500 19169
>   rand-1
41 18467 6334 26500 19169
l rand-2.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>   // time
void main() {
    int i;
    srand(time(NULL));
    for (i = 0; i < 5; i++) printf("%d ", rand());
    putchar('\n');
}
>   rand-2
28053 26813 30454 7404 2586
>   rand-2
28067 4271 3606 5354 11076
l rand-3.c
#include <stdio.h>
#include <stdlib.h>
void main() {
    int i;
    printf("RAND_MAX = %d\n", RAND_MAX);

    for (i = 0; i < 5; i++)    // int [0, 99]
        printf("%d ", rand() % 100);
    putchar('\n');
    for (i = 0; i < 5; i++)    // int [1, 100]
        printf("%d ", 1 + rand() % 100);
    putchar('\n');
    for (i = 0; i < 5; i++)    // float [0.0, 1.0]
        printf("%.3g ", (float)rand() / RAND_MAX);
    putchar('\n');
    for (i = 0; i < 5; i++)    // double [0.0, 1.0)
        printf("%.3g ", rand() / (RAND_MAX + 1.0));
    putchar('\n');
}
>   rand-3
RAND_MAX = 32767
41 67 34 0 69
25 79 59 63 65
0.174 0.859 0.711 0.514 0.304
0.015 0.0914 0.364 0.147 0.166
1.2   malloc ~ free
l alloc-1.c
#include <stdio.h>
#include <stdlib.h>

#define N   5
#define N2  (N * 2)

void out(const int a[], int n) {
    int i;
    printf("%d:", n);
    for (i = 0; i < n; i++) printf(" %d", a[i]);
    putchar('\n');
}

void main() {
    int a[N] = { 1, 2, 3, 4, 5 }, b[N] = { 10, 20, 30, 40, 50 };
    int *p, *q;

    p = (int*)malloc(sizeof a);
 // p = (int*)malloc(N * sizeof a[0]);
    if (!p) fprintf(stderr, "no space\n");
    else out(p, N);
    free(p);    // do nothing if p is NULL

    p = (int*)calloc(sizeof a[0], N);
 // p = (int*)calloc(N, sizeof a[0]);
 // p = (int*)calloc(sizeof a, 1);
 // p = (int*)calloc(1, sizeof a);
    if (!p) fprintf(stderr, "no space\n"), exit(1);
    out(p, N);

    memcpy(p, a, sizeof a);
    q = (int*)realloc(p, N2 * sizeof a[0]);
 // p = (int*)realloc(p, N2 * sizeof a[0]);
    // buggy -- how to free p if fail
    if (!q) {
        free(p);
        fprintf(stderr, "no space\n"), exit(1);
    }
    p = q;
    memcpy(p+N, b, sizeof b);
    out(p, N2);
    free(p);
}
>   alloc-1
5: 3670568 3670568 0 0 0
5: 0 0 0 0 0
10: 1 2 3 4 5 10 20 30 40 50
l alloc-2.c
#include <stdio.h>
#include <stdlib.h>

#define NCOL 4  // # of columns

void out_matrix(const char *name, const int m[][NCOL], int nrow) {
    int i, j;                     // const int (*m)[NCOL]
    printf("=== %s ===\n", name);
    for (i = 0; i < nrow; i++) {
        for (j = 0; j < NCOL; j++) printf("%d ", m[i][j]);
        putchar('\n');
    }
}

void main() {
    int m[][NCOL] = { { 1, 2 }, { 3 } };
    int (*p)[NCOL], nrow = 3;

    out_matrix("m", m, sizeof m / sizeof m[0]);

    if (!(p = (int (*)[NCOL])calloc(sizeof *p, nrow)))
        fprintf(stderr, "no space\n"), exit(1);
    out_matrix("p", p, nrow);
    free(p);
}
>   alloc-2
=== m ===
1 2 0 0
3 0 0 0
=== p ===
0 0 0 0
0 0 0 0
0 0 0 0
l alloc-2-1.c
#include <stdio.h>
#include <stdlib.h>

#define NCOL 4  // # of columns
typedef int (*PMATRIX_T)[NCOL];

void out_matrix(const char *name, const PMATRIX_T m, int nrow) {
    int i, j;
    printf("=== %s ===\n", name);
    for (i = 0; i < nrow; i++) {
        for (j = 0; j < NCOL; j++) printf("%d ", m[i][j]);
        putchar('\n');
    }
}

void main() {
    int m[][NCOL] = { { 1, 2 }, { 3 } };
    PMATRIX_T p; int nrow = 3, i, j;

    out_matrix("m", m, sizeof m / sizeof m[0]);

    if (!(p = (PMATRIX_T)malloc(nrow * NCOL * sizeof(int))))
        fprintf(stderr, "no space\n"), exit(1);
    for (i = 0; i < nrow; i++)
        for (j = 0; j < NCOL; j++) p[i][j] = i + j;
    out_matrix("p", p, nrow);
    free(p);
}
>   alloc-2-1
=== m ===
1 2 0 0
3 0 0 0
=== p ===
0 1 2 3
1 2 3 4
2 3 4 5
l alloc-3.c
#include <stdio.h>
#include <stdlib.h>

typedef struct _class { // class type
    char *name;                // class name
    struct _class *super;      // super class
} CLASS_T;

void out_relation_chain(const CLASS_T c[], int n) {
    const CLASS_T *p; int i;
    for (i = 0; i < n; i++) {
        if (!c[i].super) continue;
        fputs(c[i].name, stdout);
        for (p = c[i].super; p; p = p->super) printf(" => %s", p->name);
        putchar('\n');
    }
}

void main() {
    CLASS_T *p, *q; int i;

    // 서울시 -+-- 구로구 ---- 개봉동
    //        +-- 강남구
    // 경기도 ---- 과천시
    if (!(p = (CLASS_T*)malloc(6 * sizeof *p)))
        fprintf(stderr, "no space\n"), exit(1);
    p[0].name = "서울시"; p[0].super = 0;
    p[1].name = "구로구"; p[1].super = p+0;
    p[2].name = "개봉동"; p[2].super = p+1;
    p[3].name = "강남구"; p[3].super = p+0;
    p[4].name = "경기도"; p[4].super = 0;
    p[5].name = "과천시"; p[5].super = p+4;
    out_relation_chain(p, 6);
    
    printf("expanding memory ...\n");
    if (!(q = (CLASS_T*)realloc(p, 8 * sizeof *p)))
        free(p), fprintf(stderr, "no space\n"), exit(1);

    // adjust super pointers
    for (i = 0; i < 6; i++)
        if (q[i].super) q[i].super = q + (q[i].super - p);
    p = q;

    // (서울시) ---- 관악구 ---- 신림동
    p[6].name = "관악구"; p[6].super = p+0;
    p[7].name = "신림동"; p[7].super = p+6;
    out_relation_chain(p, 8);
    free(p);
}
>   alloc-3
구로구 => 서울시
개봉동 => 구로구 => 서울시
강남구 => 서울시
과천시 => 경기도
expanding memory ...
구로구 => 서울시
개봉동 => 구로구 => 서울시
강남구 => 서울시
과천시 => 경기도
관악구 => 서울시
신림동 => 관악구 => 서울시

n Tiny Project: Word Counting
s tree.h, tree.c, wcount.c
l tree.h
#ifndef __TREE_H__
#define __TREE_H__

// binary tree node type for word counting
typedef struct _tag_tnode {
    char *word;    // word
    int count;     // word count
    struct _tag_tnode *left, *right;  // left & right children
} TNODE_T;

// add 'word' to subtree 'p'
// return the root node pointer of the subtree
// tree: ordered by the words' lexicographic order
TNODE_T *tree_add(const char *word, TNODE_T *p);

// output subtree 'p' 
void tree_out(const TNODE_T *p);

// remove subtree 'p' 
void tree_remove(TNODE_T *p);

#endif // __TREE_H__
l tree.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // strcmp, _strdup
#include "tree.h"

TNODE_T *tree_add(const char *word, TNODE_T *p) {
    int r;
    if (!p) {
        if (!(p = (TNODE_T*)malloc(sizeof *p)) || !(p->word = _strdup(word)))
            fprintf(stderr, "no space\n"), exit(1);
        p->left = p->right = NULL; p->count = 1;
    } else if ((r = strcmp(word, p->word)) < 0)
        p->left = tree_add(word, p->left);
    else if (r > 0)
        p->right = tree_add(word, p->right);
    else
        p->count++;
    return p;
}

void tree_out(const TNODE_T *p) {     // in-order traversal
    if (!p) return;
    tree_out(p->left);
    printf("%4d: %s\n", p->count, p->word);
    tree_out(p->right);
}

void tree_remove(TNODE_T *p) {        // post-order traversal
    if (!p) return;
    tree_remove(p->left); tree_remove(p->right);
    free(p->word); free(p);
}
l wcount.c
// count words using a binary tree
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include "tree.h"

#define WORD_SIZE 10000
void main() {
    TNODE_T *root = NULL;
    char word[WORD_SIZE];

    while (scanf("%s", word) == 1)
        root = tree_add(word, root);

    tree_out(root);
    tree_remove(root);
}
>   cl wcount.c tree.c
>   wcount
if you see her you will love her
^Z
   2: her
   1: if
   1: love
   1: see
   1: will
   2: you

1.3   qsort, bsearch
l qb-1.c
// integer, increasing order
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>

int f_cmp_int(const void *p, const void *q)
    { return *(int*)p - *(int*)q; }

void main() {
    int tbl[] = { 3, -1, 0, 4, 6, 1 };
    const int SIZE = sizeof tbl / sizeof tbl[0];
    int key, i;

    qsort(tbl, SIZE, sizeof tbl[0], f_cmp_int);
    for (i = 0; i < SIZE; i++) printf("%d ", tbl[i]);
    putchar('\n');

    while (printf("key = "), scanf("%d", &key) == 1) 
        if (bsearch(&key, tbl, SIZE, sizeof tbl[0], f_cmp_int))
            printf("%d: found\n", key);
        else
            printf("%d: not found\n", key);
}
>   qb-1
-1 0 1 3 4 6
key = 3
3: found
key = 2
2: not found
key = ^Z
l qb-2.c
// double, increasing order
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>

int f_cmp_double(const double *p, const double *q) {
    if (*p == *q) return 0;
    return (*p > *q) ? +1 : -1;
}

void main() {
    double tbl[] = { 3.3, -1.1, 0.0, 3.2, 3.5, 0.1 };
    const int SIZE = sizeof tbl / sizeof *tbl;
    double key; int i;

    qsort(tbl, SIZE, sizeof *tbl,
          (int (*)(const void*, const void*))f_cmp_double);
    for (i = 0; i < SIZE; i++) printf("%g ", tbl[i]);
    putchar('\n');

    while (printf("key = "), scanf("%lf", &key) == 1) 
        if (bsearch(&key, tbl, SIZE, sizeof *tbl,
                    (int (*)(const void*, const void*))f_cmp_double))
            printf("%g: found\n", key);
        else
            printf("%g: not found\n", key);
}
>   qb-2
-1.1 0 0.1 3.2 3.3 3.5
key = -1.1
-1.1: found
key = 5
5: not found
key = ^Z
l qb-3.c
// string, increasing/decreasing order
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define USAGE   \
    "Usage: qb-3 [-r]\n"   \
    "  -r: reverse order\n"
#define SIZE (sizeof TBL / sizeof TBL[0])
char *TBL[] = { "ab", "XY", "aaa", "ZZZ", "가나다", "321", "123", "하하하" };

int f_cmp(const void *p, const void *q)
    { return strcmp(*(char**)p, *(char**)q); }
int f_cmp_r(const void *p, const void *q)
    { return strcmp(*(char**)q, *(char**)p); }

void main(int argc, char **argv) {
    char key[1000], *pk = key;
    int (*pf)(const void*, const void*), i;

    if (argc == 1) pf = f_cmp;
    else if (argc == 2 && !strcmp(argv[1], "-r")) pf = f_cmp_r;
    else fprintf(stderr, USAGE), exit(1);

    qsort(TBL, SIZE, sizeof TBL[0], pf);
    for (i = 0; i < SIZE; i++) printf("%s ", TBL[i]);
    putchar('\n');

    while (printf("key = "), scanf("%s", key) == 1) 
        if (bsearch(&pk, TBL, SIZE, sizeof TBL[0], pf))
            printf("%s: found\n", key);
        else
            printf("%s: not found\n", key);
    // bsearch(&key, TBL, SIZE, sizeof TBL[0], pf) -- logical error
    // &key, key, &key[0]: same in pointer values  but different in their types
    // &key -- char (*)[1000]; key, &key[0] -- char *
}
>   qb-3
123 321 XY ZZZ aaa ab 가나다 하하하
key = aaa
aaa: found
key = 가가가
가가가: not found
key = ^Z
>   qb-3 -r
하하하 가나다 ab aaa ZZZ XY 321 123
key = aaa
aaa: found
key = 가가가
가가가: not found
key = ^Z
l qb-3-1.c
// string, increasing/decreasing order
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define USAGE   \
    "Usage: qb-3-1 [-r]\n" \
    "  -r: reverse order\n"
#define SIZE (sizeof TBL / sizeof TBL[0])
char *TBL[] = { "ab", "XY", "aaa", "ZZZ", "가나다", "321", "123", "하하하" };

int f_cmp_sort(const void *p, const void *q)
    { return strcmp(*(char**)p, *(char**)q); }
int f_cmp_sort_r(const void *p, const void *q)
    { return strcmp(*(char**)q, *(char**)p); }
int f_cmp_find(const void *p, const void *q)
    { return strcmp((char*)p, *(char**)q); }
int f_cmp_find_r(const void *p, const void *q)
    { return strcmp(*(char**)q, (char*)p); }

void main(int argc, char **argv) {
    char key[1000]; int i;
    int (*pf_sort)(const void*, const void*);
    int (*pf_find)(const void*, const void*);

    if (argc == 1)
        { pf_sort = f_cmp_sort; pf_find = f_cmp_find; }
    else if (argc == 2 && !strcmp(argv[1], "-r"))
        { pf_sort = f_cmp_sort_r; pf_find = f_cmp_find_r; }
    else fprintf(stderr, USAGE), exit(1);

    qsort(TBL, SIZE, sizeof TBL[0], pf_sort);
    for (i = 0; i < SIZE; i++) printf("%s ", TBL[i]);
    putchar('\n');

    while (printf("key = "), scanf("%s", key) == 1) 
        if (bsearch(key, TBL, SIZE, sizeof TBL[0], pf_find))
            printf("%s: found\n", key);
        else
            printf("%s: not found\n", key);
}
l qb-4.c
// structure, increasing order
// 1st key => name(string), 2nd key => id(int)
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char *name; int id; double gpa; } STUD_T;
typedef int (*FCMP_T)(const void*, const void*);
#define SIZE (sizeof TBL / sizeof *TBL)
STUD_T TBL[] = {
    { "KIM", 202010, 3.4 },
    { "LEE", 202011, 3.3 },
    { "KIM", 202009, 3.0 },
    { "CHO", 202012, 3.5 }
};

int f_cmp(const STUD_T *p, const STUD_T *q) {
    int r = strcmp(p->name, q->name);
    if (r) return r;
    return p->id - q->id;
}
void out(const STUD_T *p, int size) {
    const STUD_T *up;
    for (up = p + size; p < up; p++)
        printf("%s %d %#.3g\n", p->name, p->id, p->gpa);
}

void main() {
    char name[1000];
    STUD_T s = { name }, *p;

    qsort(TBL, SIZE, sizeof *TBL, (FCMP_T)f_cmp);
    out(TBL, SIZE);

    while (printf("key(name id) = "), scanf("%s %d", s.name, &s.id) == 2) 
        if ((p = (STUD_T*)bsearch(&s, TBL, SIZE, sizeof *TBL, (FCMP_T)f_cmp)))
            printf("%s %d %#.3g\n", p->name, p->id, p->gpa);
        else
            printf("not found: %s %d\n", s.name, s.id);
}
>   qb-4
CHO 202012 3.50
KIM 202009 3.00
KIM 202010 3.40
LEE 202011 3.30
key(name id) = KIM 202009
KIM 202009 3.00
key(name id) = KIM 202099
not found: KIM 202099
key(name id) = ^Z
l qb-4-1.c
// structure, increasing order
// key => id(int)
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char *name; int id; double gpa; } STUD_T;
typedef int (*FCMP_T)(const void*, const void*);
#define SIZE (sizeof TBL / sizeof *TBL)
STUD_T TBL[] = {
    { "KIM", 202010, 3.4 },
    { "LEE", 202011, 3.3 },
    { "KIM", 202009, 3.0 },
    { "CHO", 202012, 3.5 }
};

int f_cmp_sort(const STUD_T *p, const STUD_T *q) 
    { return p->id - q->id; }
int f_cmp_find(int *p, const STUD_T *q)
    { return *p - q->id; }
void out(const STUD_T *p, int size) {
    const STUD_T *up;
    for (up = p + size; p < up; p++)
        printf("%s %d %#.3g\n", p->name, p->id, p->gpa);
}

void main() {
    int id; STUD_T *p;

    qsort(TBL, SIZE, sizeof *TBL, (FCMP_T)f_cmp_sort);
    out(TBL, SIZE);

    while (printf("key(id) = "), scanf("%d", &id) == 1) 
        if ((p = (STUD_T*)bsearch(&id, TBL, SIZE, sizeof *TBL,
                                  (FCMP_T)f_cmp_find)))
            printf("%s %d %#.3g\n", p->name, p->id, p->gpa);
        else
            printf("not found: %d\n", id);
}
>   qb-4-1
KIM 202009 3.00
KIM 202010 3.40
LEE 202011 3.30
CHO 202012 3.50
key(id) = 202010
KIM 202010 3.40
key(id) = 202001
not found: 202001
key(id) = ^Z

l Little Endian vs. Big Endian
-   Little Endian format: Least significant byte is stored in the lowest memory address.
e.g.) Intel Pentium, Compaq/Dec Alpha
-   Big Endian format: Most significant byte is stored in the lowest memory address.
e.g.) Motorola 680x0, Sun SPARC
  
Ø Historical note: The names Big Endian and Little Endian come from Swift’s novel Gulliver’s Travels. In this story the Lilliputians were divided into the Big Endians and the Little Endians based on which end of a boiled egg they believed should be opened.
-   Unicode files may include a BOM(Byte Order Mark) to help distinguish the Big Endian and Little Endian.

BOM
Encoding
EF BB BF
UTF-8
FE FF
UTF-16 (big-endian)
FF FE
UTF-16 (little-endian)
00 00 FE FF
UTF-32 (big-endian)
FF FE 00 00
UTF-32 (little-endian)

l endian.h
#ifndef __ENDIAN_H__
#define __ENDIAN_H__

// assumption for data sizes
// short: 2B, int: 4B, long: 4B, long long: 8B, float: 4B, double: 8B
typedef unsigned short UINT2;
typedef unsigned int UINT4;
typedef unsigned long long UINT8;

void endian_swap2(UINT2 *p);    // byte swap for 2B data
void endian_swap4(UINT4 *p);    // byte swap for 4B data
void endian_swap8(UINT8 *p);    // byte swap for 8B data

enum ENDIAN_T { E_UNKNOWN, E_LITTLE, E_BIG };
enum ENDIAN_T get_endian();     // return host's endianness

#endif // __ENDIAN_H__
l endian.c
#include "endian.h"

void endian_swap2(UINT2 *p) {
    *p = (*p>>8) | (*p<<8);
}
void endian_swap4(UINT4 *p) {
    *p = (*p>>24) | 
        ((*p>> 8) & 0x0000FF00) |
        ((*p<< 8) & 0x00FF0000) |
         (*p<<24);
}
void endian_swap8(UINT8 *p) {
    *p = (*p>>56) | 
        ((*p>>40) & 0x000000000000FF00) |
        ((*p>>24) & 0x0000000000FF0000) |
        ((*p>> 8) & 0x00000000FF000000) |
        ((*p<< 8) & 0x000000FF00000000) |
        ((*p<<24) & 0x0000FF0000000000) |
        ((*p<<40) & 0x00FF000000000000) |
         (*p<<56);
}

enum ENDIAN_T get_endian() {
    static union { unsigned char s[2]; UINT2 n; } bom = { 0xFE, 0xFF };
    return (bom.n == 0xFEFF) ? E_BIG :
           (bom.n == 0xFFFE) ? E_LITTLE : E_UNKNOWN;
}
l end.c
#include <stdio.h>
#include "endian.h"

void out_bytes(void *p, int n) {
    int i;
    printf("%02X", *((unsigned char*)p));
    for (i = 1; i < n; i++) printf(":%02X", ((unsigned char*)p)[i]);
}

void main() {
    short h = 0xAABB; int i = 0xAABBCCDD;
    float f = 1.2345; double d = 1.2345;
    enum ENDIAN_T e_value; const char *e_name;

    printf("sizeof(UINT2) = %d\n", sizeof(UINT2));
    printf("sizeof(UINT4) = %d\n", sizeof(UINT4));
    printf("sizeof(UINT8) = %d\n", sizeof(UINT8));

    printf("[%11hd] ", h); out_bytes(&h, sizeof h);
    endian_swap2(&h);
    printf(" => "); out_bytes(&h, sizeof h); putchar('\n');

    printf("[%11d] ", i); out_bytes(&i, sizeof i);
    endian_swap4(&i);
    printf(" => "); out_bytes(&i, sizeof i); putchar('\n');

    printf("[%11g] ", f); out_bytes(&f, sizeof f);
    endian_swap4((UINT4*)&f);
    printf(" => "); out_bytes(&f, sizeof f); putchar('\n');

    printf("[%11g] ", d); out_bytes(&d, sizeof d);
    endian_swap8((UINT8*)&d);
    printf(" => "); out_bytes(&d, sizeof d); putchar('\n');

    if ((e_value = get_endian()) == E_BIG) e_name = "big";
    else if (e_value = E_LITTLE) e_name = "little";
    else e_name = "unknown";
    printf("This machine is %s endian.\n", e_name);
}
>   cl end.c endian.c
>   end
sizeof(UINT2) = 2
sizeof(UINT4) = 4
sizeof(UINT8) = 8
[      -21829] BB:AA => AA:BB
[-1430532899] DD:CC:BB:AA => AA:BB:CC:DD
[      1.2345] 19:04:9E:3F => 3F:9E:04:19
[      1.2345] 8D:97:6E:12:83:C0:F3:3F => 3F:F3:C0:83:12:6E:97:8D
This machine is little endian.

n Tiny Project: Student Data Handling (2)
s endian.h, endian.c, stud.h, stud.c,
make_sdat.c, show_sdat.c, conv_sdat.c, find_sdat.c
l endian.h, endian.c
l stud.h
#ifndef __STUD_H__
#define __STUD_H__
#include "endian.h"

#define SNAME_SIZE  10   // student name size
typedef struct {         // student record type
    int    id;                 // student ID
    char   name[SNAME_SIZE+1]; // student name
    double gpa;                // grade point average
} STUD_T;

// output a student record
void out_stud(FILE *fp, const STUD_T *ps);

// format of binary student data file: BOM(UINT2) + student records
#define BOM 0xFEFF
#define BOM0_BIG 0xFE

// save student data as given, return 0 if succeed
int save_sdat(const STUD_T *sdat, UINT4 n, UINT2 bom, const char *fn);
// load student data, return 0 if succeed
// convert student records to fit for host endian format if necessary
int load_sdat(STUD_T **psdat, UINT4 *pn, UINT2 *pbom, const char *fn);
// byte swap for student records
void swap_sdat(STUD_T *p, UINT4 n);

#endif
l stud.c
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include "stud.h"

void out_stud(FILE *fp, const STUD_T *ps) {
    fprintf(fp, "%8d => %-*s %.2f\n",
            ps->id, SNAME_SIZE, ps->name, ps->gpa);
}

int save_sdat(const STUD_T *sdat, UINT4 n, UINT2 bom, const char *fn) {
    FILE *fp;
    if (!(fp = fopen(fn, "wb")))
        return fprintf(stderr, "%s: fail to open", fn), 1;
    if (fwrite(&bom, sizeof bom, 1, fp) != 1 ||
        fwrite(sdat, sizeof *sdat, n, fp) != n)
        return fclose(fp), fprintf(stderr, "%s: fail to write", fn), 1;
    return fclose(fp), 0;
}
    
int load_sdat(STUD_T **psdat, UINT4 *pn, UINT2 *pbom, const char *fn) {
    STUD_T *sdat; UINT4 n; UINT2 bom; FILE *fp; long size;
    if (!(fp = fopen(fn, "rb")))
        return fprintf(stderr, "%s: fail to open", fn), 1;
    if (fseek(fp, 0, SEEK_END) || (size = ftell(fp)) < 0)
        return fclose(fp), fprintf(stderr, "%s: bad data", fn), 1;
    size -= sizeof bom; rewind(fp);
    if (fread(&bom, sizeof bom, 1, fp) != 1 || size % sizeof(STUD_T) != 0)
        return fclose(fp), fprintf(stderr, "%s: bad data", fn), 1;
    n = size / sizeof(STUD_T);
    if (!(sdat = (STUD_T*)malloc(n * sizeof *sdat)))
        return fclose(fp), fprintf(stderr, "%s: no space", fn), 1;
    if (fread(sdat, sizeof *sdat, n, fp) != n)
        return free(sdat), fclose(fp), fprintf(stderr, "%s: bad data", fn), 1;
    if (bom != BOM) swap_sdat(sdat, n);
    *psdat = sdat; *pn = n; *pbom = bom;
    return fclose(fp), 0;
}

void swap_sdat(STUD_T *p, UINT4 n) {
    STUD_T *up;
    printf("swapping student records ...\n");
    for (up = p + n; p < up; p++)
        endian_swap4(&p->id), endian_swap8((UINT8*)&p->gpa); 
}
l make_sdat.c
// sort & store student data
#include <stdio.h>
#include <stdlib.h>
#include "stud.h"

#define USAGE "Usage: make_sdat output_data_file\n"
#define NO_SDAT (sizeof SDAT / sizeof SDAT[0])
STUD_T SDAT[] = {
    { 2000005, "을지문덕", 3.88 },
    { 2000001, "강감찬",   2.81 },
    { 2000008, "이순신",   3.77 },
    { 2000006, "이순신",   2.99 },
    { 2000002, "곽재우",   3.62 },
};
int f_cmp(const STUD_T *p, const STUD_T *q) { return p->id - q->id; }
    
int main(int argc, char **argv) {
    int i;
    if (argc != 2) return fprintf(stderr, "%s", USAGE), 1;

    qsort(SDAT, NO_SDAT, sizeof *SDAT,
          (int (*)(const void*, const void*))f_cmp);
    for (i = 1; i < NO_SDAT; i++)
        if (SDAT[i-1].id == SDAT[i].id)
            return fprintf(stderr, "duplicate IDs: %d\n", SDAT[i].id), 1;
    if (save_sdat(SDAT, NO_SDAT, BOM, argv[1])) return 1;
    return 0;
}
l show_sdat.c
// show student data
#include <stdio.h>
#include "stud.h"
#include "endian.h" // **
#define USAGE "Usage: show_sdat input_data_file\n"
int main(int argc, char **argv) {
    STUD_T *sdat; UINT4 n, i; UINT2 bom; const char *e_name;
    if (argc != 2) return fprintf(stderr, "%s", USAGE), 1;

    if (load_sdat(&sdat, &n, &bom, argv[1])) return 1;
    e_name = (*(unsigned char*)&bom == BOM0_BIG) ? "big" : "little";
    printf("format: %s endian, # of records: %u\n", e_name, n);
    for (i = 0; i < n; i++) out_stud(stdout, sdat+i);
    return free(sdat), 0;
}
l conv_sdat.c
// convert endianness of student data: big <-> little
#include <stdio.h>
#include "stud.h"
#define USAGE "Usage: conv_sdat input_data_file output_data_file\n"
int main(int argc, char **argv) {
    STUD_T *sdat; UINT4 n; UINT2 bom;
    if (argc != 3) return fprintf(stderr, "%s", USAGE), 1;

    if (load_sdat(&sdat, &n, &bom, argv[1])) return 1;

    if (*(unsigned char*)&bom == BOM0_BIG)
        printf("endian conversion: big => little\n");
    else
        printf("endian conversion: little => big\n");
    endian_swap2(&bom);
    if (bom != BOM) swap_sdat(sdat, n);

    if (save_sdat(sdat, n, bom, argv[2]))
        return free(sdat), 1;
    return free(sdat), 0;
}
l find_sdat.c
// search student data
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include "stud.h"

#define USAGE   "Usage: find_sdat input_data_file\n"
int f_cmp(int *pid, const STUD_T *p) { return *pid - p->id; }
    
int main(int argc, char **argv) {
    STUD_T *sdat, *p; UINT4 n; UINT2 bom; int id;
    if (argc != 2) return fprintf(stderr, "%s", USAGE), 1;
    if (load_sdat(&sdat, &n, &bom, argv[1])) return 1;

    while (printf("student ID = "), scanf("%d", &id) == 1)
        if ((p = (STUD_T*)bsearch(&id, sdat, n, sizeof *sdat,
                                  (int (*)(const void*, const void*))f_cmp)))
            out_stud(stdout, p);
        else
            printf("%d: not found\n", id);

    return free(sdat), 0;
}
>   cl -c endian.c stud.c
>   cl make_sdat.c endian.obj stud.obj
>   cl show_sdat.c endian.obj stud.obj
>   cl conv_sdat.c endian.obj stud.obj
>   cl find_sdat.c endian.obj stud.obj
>   make_sdat s.dat
>   show_sdat s.dat
format: little endian, # of records: 5
 2000001 => 강감찬     2.81
 2000002 => 곽재우     3.62
 2000005 => 을지문덕   3.88
 2000006 => 이순신     2.99
 2000008 => 이순신     3.77
>   conv_sdat s.dat s1.dat
endian conversion: little => big
swapping student records ...
>   show_sdat s1.dat
swapping student records ...
format: big endian, # of records: 5
 2000001 => 강감찬     2.81
 ……
>   conv_sdat s1.dat s2.dat
swapping student records ...
endian conversion: big => little
>   show_sdat s2.dat
format: little endian, # of records: 5
 2000001 => 강감찬     2.81
……
>   find_sdat s.dat
student ID = 2000002
 2000002 => 곽재우     3.62
student ID = 2000003
2000003: not found
student ID = ^Z
>   find_sdat s1.dat
swapping student records ...
student ID = 2000008
 2000008 => 이순신     3.77
student ID = 2000009
2000009: not found
student ID = ^Z

1.4   abort ~ getenv
l abort.c
#include <stdio.h>
#include <stdlib.h>

void f(int *p) { if (!p) abort(); }

void main() { f(NULL); }
>   abort
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
>   echo %ERRORLEVEL%
3
l exit-1.c
#include <stdio.h>
#include <stdlib.h>

void f(int *p) { if (!p) exit(EXIT_FAILURE); }

void main() {
    printf("EXIT_SUCCESS = %d\n", EXIT_SUCCESS);
    printf("EXIT_FAILURE = %d\n", EXIT_FAILURE);
    f(NULL);
    fprintf(stderr, "End of main()\n");
}
>   exit-1
EXIT_SUCCESS = 0
EXIT_FAILURE = 1
>   echo %ERRORLEVEL%
1
l exit-2.c
#include <stdio.h>
#include <stdlib.h>

void f_exit1(void) { fprintf(stderr, "[1] Exiting ...\n"); }
void f_exit2(void) { fprintf(stderr, "[2] Exiting ...\n"); }

void main() {
    if (atexit(f_exit1) || atexit(f_exit2))
        fprintf(stderr, "fail to call atexit()\n"), exit(EXIT_FAILURE);
}
>   exit-2
[2] Exiting ...
[1] Exiting ...
>   echo %ERRORLEVEL%
0
l exit_0.c
#include <stdio.h>
#include <stdlib.h>
void main() { printf("exit(0)\n"); exit(0); }
>   exit_0
exit(0)
>   echo %ERRORLEVEL%
0
l exit_-99999.c
#include <stdio.h>
#include <stdlib.h>
void main() { printf("exit(-99999)\n"); exit(-99999); }
>   exit_-99999
exit(-99999)
>   echo %ERRORLEVEL%
-99999
l return_0.c
#include <stdio.h>
int main() { printf("return 0\n"); return 0; }
>   return_0
return 0
>   echo %ERRORLEVEL%
0
l return_-99999.c
#include <stdio.h>
int main() { printf("return -99999\n"); return -99999; }
>   return_-99999
return -99999
>   echo %ERRORLEVEL%
-99999
l sys-1.c
#include <stdio.h>
#include <stdlib.h>

void job(const char *command) {
    fprintf(stderr, "--> system(\"%s\")\n", command);
    if (system(command)) fprintf(stderr, "<-- fail: ");
    else fprintf(stderr, "<-- done: ");
    fprintf(stderr, "system(\"%s\")\n\n", command);
}

void main() {
    if (system(NULL)) printf("command processor: exist\n\n");
    else printf("command processor: not exist\n\n");

    job("cmd.exe");
    job("notepad");
    job("dir /B *.c");
    job("???");
}
>   sys-1
command processor: exist

--> system("cmd.exe")
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

E:\lecture\C3\stdlib\4>exit
<-- done: system("cmd.exe")

--> system("notepad")
<-- done: system("notepad")

--> system("dir /B *.c")
abort.c
env-1.c
env-2.c
exit-1.c
exit-2.c
exit_-99999.c
exit_0.c
return_-99999.c
return_0.c
sys-1.c
sys-2.c
<-- done: system("dir /B *.c")

--> system("???")
'???'() 내부 또는 외부 명령, 실행할 있는 프로그램, 또는
배치 파일이 아닙니다.
<-- fail: system("???")
l sys-2.c
#include <stdio.h>
#include <stdlib.h>

void job(const char *command) {
    int r;
    fprintf(stderr, "--> system(\"%s\")\n", command);
    if ((r = system(command))) fprintf(stderr, "<-- fail: [%d]\n\n", r);
    else fprintf(stderr, "<-- done\n\n");
}

void main() {
    job("exit_0");
    job("exit_-99999");
    job("return_0");
    job("return_-99999");
    job("abort.exe");
    job("???");
}
>   sys-2
--> system("exit_0")
exit(0)
<-- done

--> system("exit_-99999")
exit(-99999)
<-- fail: [-99999]

--> system("return_0")
return 0
<-- done

--> system("return_-99999")
return -99999
<-- fail: [-99999]

--> system("abort.exe")

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
<-- fail: [3]

--> system("???")
'???'() 내부 또는 외부 명령, 실행할 있는 프로그램, 또는
배치 파일이 아닙니다.
<-- fail: [1]
l env-1.c
#include <stdio.h>
void main(int argc, char **argv, char **envp) {
    while (*envp) printf("%s\n", *envp++);
}
>   env-1
OALLUSERSPROFILE=C:\Documents and Settings\All Users
APPDATA=C:\Documents and Settings\owner\Application Data
……
>   set
OALLUSERSPROFILE=C:\Documents and Settings\All Users
APPDATA=C:\Documents and Settings\owner\Application Data
……
l env-2.c
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
void main() {
    char *p;
    if ((p = getenv("CLASS"))) printf("CLASS=%s\n", p);
    else printf("CLASS: not found\n");
    if ((p = getenv(" CLASS"))) printf(" CLASS=%s\n", p);
    else printf(" CLASS: not found\n");
    if ((p = getenv("class"))) printf("class=%s\n", p);
    else printf("class: not found\n");
}
>   set CLASS
CLASS 환경 변수가 정의되지 않았습니다.
>   env-2
CLASS: not found
 CLASS: not found
class: not found
>   set CLASS=C Programming 3
>   set
……
CLASS=C Programming 3
……
>   set CLASS
CLASS=C Programming 3
>   env-2
CLASS=C Programming 3
 CLASS: not found
class=C Programming 3

No comments: