Thursday, June 16, 2011

Standard C Library With EXAMPLE CODES

This lecture note provides a summary of the standard C library.
External identifiers that begin with underscores are reserved for use by the library.
Standard headers covered here are:

<errno.h>
error codes reported by library functions
<assert.h>
diagnostics
<stdarg.h>
variable argument lists
<stdio.h>
input and output
<ctype.h>
character class tests
<string.h>
string functions
<stdlib.h>
utility functions
<math.h>
mathematical functions
<time.h>
time and date functions
<setjmp.h>
non-local jumps
<signal.h>
signals
<limits.h>
implementation-defined integral type limits
<float.h>
implementation-defined floating-point limits

The following standard headers are omitted here:

<stddef.h>
definitions of general use
<locale.h>
locale-specific information
<wchar.h>
wide strings and streams
<wctype.h>
wide character class tests
<iso646.h>
ISO 646 variant character sets

Error Codes Reported by Library Functions

1.  <errno.h>  Error Codes Reported by Library Functions

The header <errno.h> defines macros to report error conditions through error codes.
The errno macro expands to an external variable of type int, containing the last error code generated in any function using the errno facility.
The following macros expand to integer constants which represent the error codes:

EDOM
domain errors
e.g.) sqrt(-1.0) (in <math.h>)
ERANGE
range errors
e.g.) atoi("12345678901")  (in <stdlib.h>)

If you intend to test the value of errno after library function calls, first set it to 0, because the library functions do not reset the value to 0.
perror or strerror functions can be used to print the description of the message associated with a particular errno.

void perror(const char *s) (in <stdio.h>)
perror(s) prints s and an implementation-defined error message corresponding to errno, as if by
fprintf(stderr, "%s: %s\n", s, "error message")
char *strerror(int e) (in <string.h>)
strerror(e) returns a pointer to an implementation-defined error message corresponding to e.



EXAPMLE CODES

1.       error.h
l err.c
#define _CRT_SECURE_NO_DEPRECATE
    // to disable deprecation for standard C functions in VC
    // must be defined before headers are included
#include <errno.h>
#include <stdio.h>  // perror
#include <string.h> // strerror
#include <stdlib.h> // atoi
#include <math.h>   // sqrt
void main() {
    printf("EDOM = %d, ERANGE = %d\n\n", EDOM, ERANGE);

    printf("[At Startup]\n");
    printf("errno = %d\n", errno);
    perror("ERROR");
    fprintf(stderr, "%s: %s\n\n", "ERROR", strerror(errno));

    printf("errno = %d\n", errno = ERANGE);
    perror(NULL);
    perror("ERROR");
    fprintf(stderr, "%s: %s\n\n", "ERROR", strerror(errno));

    printf("sqrt(-1.0) = %g\n", sqrt(-1.0));
    fprintf(stderr, "ERROR: %d -- %s\n\n", errno, strerror(errno));

    printf("sqrt(4.0) = %g\n", sqrt(4.0));
    fprintf(stderr, "ERROR: %d -- %s\n\n", errno, strerror(errno));

    errno = 0; // clear error
    printf("sqrt(4.0) = %g\n", sqrt(4.0));
    fprintf(stderr, "ERROR: %d -- %s\n\n", errno, strerror(errno));

    errno = 0;
    printf("atoi(\"12345678901\") = %d\n", atoi("12345678901"));
    fprintf(stderr, "ERROR: %d -- %s\n\n", errno, strerror(errno));
}
>   err
EDOM = 33, ERANGE = 34

[At Startup]
errno = 0
ERROR: No error
ERROR: No error

errno = 34
Result too large
ERROR: Result too large
ERROR: Result too large

sqrt(-1.0) = -1.#IND
ERROR: 33 -- Domain error

sqrt(4.0) = 2
ERROR: 33 -- Domain error

sqrt(4.0) = 2
ERROR: 0 -- No error

atoi("12345678901") = 2147483647
ERROR: 34 -- Result too large
n Output of infinite and indeterminate values
s -1.#IND: indeterminate value
s ±1.#INF: infinity – e.g.) double x = 0.0; printf("%g", 1.0 / x);

1.  <assert.h>  Diagnostics

The assert macro is used to add diagnostics to programs:
void assert(int expression)
If expression is zero (false) when assert(expression) is executed, the assert macro will print on stderr a message, such as
Assertion failed: expression, file filename, line nnn
It then calls abort (in <stdlib.h>) to terminate execution. The source filename and line number come from the preprocessor macros __FILE__ and __LINE__.
If NDEBUG is defined at the time <assert.h> is included, the assert macro is ignored.

EXAMPLE CODES

1.       assert.h
l assert.c
#include <stdio.h>
#include <assert.h>
void out(int *p) {
    assert(p != NULL);
    printf("%d\n", *p);
}
void
 main() {
    
int
 n = 10;
    out(&n);
    out(NULL);
}
>   cl assert.c && assert
10
Assertion failed: p != NULL, file assert.c, line 4

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
>   cl -D "NDEBUG" assert.c && assert
10
n VC IDE: Project Properties 참고










n VC IDE: Debug Release Configuration에서 각각 Build 실행

3.  <stdarg.h>  Variable Argument Lists

If a parameter list ends with an ellipsis "..." in a function, then the function may accept more arguments than the number of parameters explicitly described, which are called unnamed arguments.
The header <stdarg.h> provides facilities for stepping through a variable argument list or a list of unnamed function arguments.
Suppose lastarg is the last named parameter of a function f with a variable number of arguments. Then declare within f a variable of type va_list that will point to each argument in turn:
va_list ap;
ap must be initialized once with the macro va_start before any unnamed argument is accessed:
va_start(va_list ap, lastarg);
Thereafter, each execution of the macro va_arg will produce a value that has the type and value of the next unnamed argument, and will also modify ap so the next use of va_arg returns the next argument:
type  va_arg(va_list ap, type);
The macro
void va_end(va_list ap);
must be called once after the arguments have been processed but before f is exited.
When unnamed arguments are passed, default argument promotion is applied to each argument as follows: integral promotion is performed on each argument of integral type, and each float argument is converted to double. 
A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type may be used in an expression wherever an integer may be used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion.

EXAMPLE CODES

1.       stdarg.h
l sum-1.c
#include <stdio.h>
#include <stdarg.h>
int sum(int n, ...) {  // sum of n int values -- variadic function
    int i, res = 0;
    va_list ap;          // pointer to unnamed arg
    va_start(ap, n);    // make ap point to 1st unnamed arg
    for (i = 0; i < n; i++)
        res += va_arg(ap, int);
    va_end(ap);          // clean up
    return res;
}
void main() {
    printf("%d %d\n", sum(2, 1, 2), sum(3, 10, 20, 30, 40));
}
>   sum-1
3 60
l sum-2.c
#include <stdio.h>
#include <stdarg.h>
int vsum(int n, va_list arg) {  // sum of n int values
    int i, res = 0;
    for (i = 0; i < n; i++)
        res += va_arg(arg, int);
    return res;
}
int sum(int n, ...) {           // sum of n int values
    int res;
    va_list arg;
    va_start(arg, n);
    res = vsum(n, arg);
    va_end(arg);
    return res;
}
void main() {
    printf("%d %d\n", sum(2, 1, 2), sum(3, 10, 20, 30, 40));
}
>   sum-2
3 60
l print.c
#include <stdio.h>
#include <stdarg.h>
// conversion specifications: %c, %d %f %s %%
void print(const char *format, ...) {
    const char *p;
    va_list ap;
    va_start(ap, format);
    for (p = format; *p; p++) {
        if (*p != '%') { putchar(*p); continue; }
        switch (*++p) {
            case 'd': printf("%d", va_arg(ap, int)); break;
            case 'f': printf("%f", va_arg(ap, double)); break;
            case 's': fputs(va_arg(ap, char *), stdout); break;
            case 'c': putchar(va_arg(ap, int)); break;
            default:  putchar(*p); break;
        }
    }
    va_end(ap);
}
void main() {
    print("100%% %c %d %d %d %f %f %s\n",
           'A', 'A', (short)10, 10, 3.4, 3.4f, "SKHU CC");
}
>   print
100% A 65 10 10 3.400000 3.400000 SKHU CC
l trace.c
#include <stdio.h>
#include <stdarg.h>
#ifdef WIN32    // VC
    #define __func__ __FUNCTION__  // function name
#endif
// variadic macro
#ifdef _DEBUG   // VC debug configuration 
    #define TRACE(...) trace(__FILE__, __func__, __LINE__, __VA_ARGS__)
#else
    #define TRACE(...)
#endif
void trace(const char *file, const char *func, int line, const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    printf("[%s:%s:%d] ", file, func, line);
    vprintf(fmt, ap);
    va_end(ap);
}
int sum(int a, int b) {
    int res;
    TRACE("a = %d, b = %d\n", a, b);
    res = a + b;
    TRACE("res = %d\n", res);
    return res;
}
void main() {
    printf("sum(1, 2) = %d\n", sum(1, 2));
}
>   cl trace.c && trace
$ gcc trace.c -o trace; ./trace
sum(1, 2) = 3
>   cl /D_DEBUG -DWIN32 trace.c && trace
$ gcc -D_DEBUG trace.c -o trace; ./trace
[trace.c:sum:21] a = 1, b = 2
[trace.c:sum:23] res = 3
sum(1, 2) = 3
n VC IDE: Debug Release Configuration에서 각각 Build 실행