이 chapter에서는 역자가 추가한 질문들을 정리해 놓았습니다.
#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> /* 아래 변수는 error()가 몇 번 불렸는지 횟수를 * 저장합니다. */ unsigned int error_message_count; /* 아래 함수 포인터가 NULL이면 error() 함수는 * stdout을 flush시키고 stderr로 프로그램 이름을 * 출력하고, ": "를 출력합니다. */ void (*error_print_progname); /* error() 함수를 쓰는 프로그램은 반드시 * program_name이라는 변수를 정의하고 프로그램의 * 이름(실행 파일 이름)을 지정해야 합니다. */ extern const char *program_name; void error(int status, int errnum, const char *msg, ...) { va_list argptr; if (error_print_progname) (*error_print_progname)(); else { fflush(stdout); fprintf(stderr, "%s: ", program_name); } va_start(argptr, msg); vfprintf(stderr, msg, argptr); va_end(argptr); ++error_message_count; if (errnum) fprintf(stderr, ": %s", strerror(errnum)); putc('\n', stderr); fflush(stderr); if (status) exit(status); }
일단 위와 같이 error() 함수를 만들었으면 여러분의 코드에서 다음과 같이 쓸 수 있습니다.
#include <stdio.h> #include <errno.h> const char *program_name = "sample" int main(void) { FILE *fp; char *filename = "sample.dat"; fp = fopen(filename, "r"); if (fp == NULL) error(EXIT_FAILURE, errno, "cannot open file %s.", filename); /* ... */ }
위의 코드를 설명하겠습니다.
error()의 첫번째 인자는 0이 아닌 경우,
라이브러리 함수 exit()로 전달됩니다. 따라서
<stdlib.h>
에 정의된 0이 아닌 값을 가지는
EXIT_FAILURE 상수를 써서 error()가 메시지를
출력하고 프로그램을 종료하도록 합니다
error()에 전달된 두번째 인자인,
errno는 <errno.h>
에 정의된 전역 변수로서
몇몇 라이브러리 함수들이 보고한 에러의 종류를 나타내는
변수입니다. 이 값은 error()가 strerror 함수를
써서 에러를 나타내는 문자열로 바꾼 다음 출력합니다.
시스템과 라이브러리 함수가 보고한 에러가 아니라면
error()의 두번째 인자로 0을 주면 됩니다.
error()의 세번째 인자부터는 printf()가 쓰는 형식 그대로 쓰시면 됩니다.
물론 위의 예제가 완벽하지는 않습니다. 여러분은 error() 함수를 쓰기 위해, 앞에서 제시한 error() 정의와, 이 함수가 쓰는 전역 변수들을 적당하게 추가해야 합니다.
에러 함수의 선언과 va_list, va_start(), va_end()에 관한 것은 15 절을 참고하기 바랍니다.
따라서 다음과 같은 함수를 만들어 쓰는 것이 좋습니다:
#include <errno.h> int convint(long int *d, const char *s) { char *p; long int i; if (*s == '\0') /* error: nothing in the source string */ return -1; i = strtol(s, &p, 0); if (errno == ERANGE) /* error: underflow or overflow */ return -1; if (*p != '\0') /* error: there is unexpected character */ return -1; *d = i; return 0; }
위 함수는 두 개의 인자를 받습니다. 첫 인자는 변환한 long int를
저장할
포인터를 받고, 두 번째 인자는 변환할 문자열을 받습니다.
리턴 값은 성공적으로 변환한 경우에는 0을, 그렇지 않은 경우에는 을
리턴합니다 - 입력에 예상할 수 없는 문자값이 오거나, 입력이 아예
없거나(문자열이 '\0'
인 경우), 변환한 값이 long int에
담을 수 없을 만큼 크거나 작은 경우, 에러로 처리합니다.
실제 사용한 예는 다음과 같습니다:
void foo(void) { char instr[80]; int i; /* instr이 user input을 가지고 있다고 가정 */ if (convint(&i, instr) < 0) { /* error: invalid user input */ } /* ... */ }
주의할 것은 이 convint 함수가 atoi와 똑같이 동작하는 게 아니라는 것입니다 -- 단순히 리턴값과 인자만의 문제가 아닙니다. atoi와 convint의 다른 점은 크게 다음과 같습니다:
"12xyz"
인 경우에 12를 리턴합니다.
convint는 입력 문자열이 완전한 경우에만 처리하므로,
입력이 "12xyz"
인 경우에는 int 변환을 수행하지 않습니다
-- 즉 에러로 처리합니다.
"0x"
를 붙여야
하며, 8진수의 경우에 문자열 앞에 "0"
을 붙여야 합니다.
http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html
getline 함수는, 미리 malloc 또는 realloc을 써서
할당한 버퍼와 그 크기를 입력받아서, 한 줄을 읽어 버퍼에 저장해 줍니다.
이 때 \n
문자도 포함합니다.
만약 버퍼의 크기가 부족하면 realloc을 불러서, 버퍼의 크기를 자동으로
키워 줍니다. EOF를 만나거나 에러가 발생하면 -1을 리턴하고, 성공한 경우에는
문자열의 길이를 리턴합니다. 아래는 간단히 만든 getline 함수입니다:
ssize_t getline(char **lineptr, size_t *n, FILE *fp) { char *buf, *ptr; size_t newsize, numbytes; int cont; int ch; int pos; if (lineptr == NULL || n == NULL || fp == NULL) return -1; buf = *lineptr; if (buf == NULL || *n < MIN_LINE_SIZE) { buf = realloc(*lineptr, DEFAULT_LINE_SIZE); if (!buf) return -1; *lineptr = buf; *n = DEFAULT_LINE_SIZE; } numbytes = *n; ptr = buf; cont = 1; while (cont) { /* fill buffer - leaving room for nul-terminator */ while (--numbytes > 0) { if ((ch = getc(fp)) == EOF) { cont = 0; break; } else { *ptr++ = ch; if (ch == '\n') { cont = 0; break; } } } if (cont) { /* Buffer is too small so reallocate a larger buffer. */ pos = ptr - buf; newsize = (*n << 1); buf = realloc(buf, newsize); if (!buf) { cont = 0; break; } /* After reallocating, continue in new buffer */ *lineptr = buf; *n = newsize; ptr = buf + pos; numbytes = newsize - pos; } } /* if no input data, return failure */ if (ptr == buf) return -1; /* otherwise, nul-terminate and return number of bytes read */ *ptr = '\0'; return (ssize_t)(ptr - buf); }
위 함수를 써서, 표준 입력(stdin)에서 한 줄씩 읽어서 출력하는 함수는 다음과 같이 만들 수 있습니다:
int main(void) { char *line = NULL; ssize_t len; size_t size; while ((len = getline(&line, &size, stdin)) >= 0) { printf("%s", line); } return 0; }
성능 좋고, 공개용인 GDB(GNU symbolic debugger)를 추천합니다. GDB를 바로 command-line에서 사용하는 것은 가장 쉬운 방법이긴 하지만, vi를 쓰지 않고 ed를 써서 작업하는 격이 됩니다.
GNU Emacs에서는 GDB를 편하게 쓸 수 있는 mode를 제공하며, DDD는 GDB를 써서 X window system에서 편하게 쓸 수 있는 interface를 제공합니다.
일단 main 함수 내에서 선언된 local 배열이 있나 조사하고, 있다면, 그 배열의 범위를 벗어난 작업이 있었는지 확인하시기 바랍니다.
Encoding | Encoding Length | Locale |
ASCII | one-byte | not applicable |
ISO-2022 | one- and two-byte | CJKV |
EUC | one- through four-byte, depending on locale | CJKV |
GBK | one- and two-byte | China |
Big Five | one- and two-byte | Taiwan |
Big Five Plus | one- and two-byte | Taiwan |
Shift-JIS | one- and two-byte | Japan |
Johab | one- and two-byte | Korea |
UHC | one- and two-byte | Korea |
UTF-8 | one- through six-byte | not applicable |
Encoding | Encoding Length |
UCS-2 | 16-bit fixed |
UCS-4 | 32-bit fixed |
UTF-16 | 16-bit variable-length |
Unicode Version 2.0 | Same as UTF-16 |
Seong-Kook Shin