<stdarg.h>
<stdlib.h>
<time.h>
C89 headers인 위 세가지 헤더에 대해 다룬다.
<stdarg.h>
HeaderVariable Arguments
이 헤더에서 제공하는 함수를 통해 printf
같이 다양한 숫자의 arguments를 다룰 수 있다.
<stdarg.h>
)va_list
위 type 하나만 define 한다.
<stdarg.h>
)아래는 모두 function like macro
void va_start(va_list ap, parmN);
type va_arg(va_list ap, type);
void va_end(va_list ap);
사용예시)
//argument 중 가장 큰 값 찾는 함수
//첫번째 인자는 argument의 개수(본인 제외)
int max_int(int n, ...) //n must be at least 1
{
va_list ap; //n뒤의 argument에 접근하려면 선언 필수
int i, current, largest;
va_start(ap, n); //어디서 variable length part가 시작하는지 알려줌
largest = va_arg(ap, int); //argument를 가져와서 반환, 하나씩 진행한다.
//두번째 인자의 type을 expect
for(i = 1; i < n; i ++) {
current = va_arg(ap, int);
if (current > largest)
largest = current;
}
va_end(ap); //함수 return 전에 clean-up. 아니면 va_start로 처음부터 다시 볼 수 있음
return larget;
}
자세한 설명은 책 참고
얘도 function like macro
void va_copy(va_list dest, va_list src); //C99
src
값을 dest
에 복사한다.
variable arguments list에서 해당 argument의 위치도 같이 저장한다.
원본에 영향을 주지 않고 argument를 processing 할 수 있다.
va_start
와 va_copy
는 꼭 va_end
와 짝을 이루어야 하며,
va_arg
는 그 사이에만 올 수 있다.
모두 같은 함수 내에 있어야 한다.
Variable Argument List를 가진 함수는 printf
나 scanf
처럼 잘못된 argument가 들어오는 것에 민감하다.
몇개의 arguments가 들어올지 모른다는 것도 문제가 되는데,
위 예시에선 n
을 입력받음으로써 해결했다.(printf
는 format string에 의존해 해결)
또 NULL
pointer는 0
을 나타내기때문에 int
로 인식할 수 있다. 따라서 (void *) NULL
형식으로 cast해서 넘겨주는게 낫다.(Chapter 17 Q&A 참고)
v...printf
Functions (<stdio.h>
)<stdio.h>
에 있는 함수들이지만, <stdarg.h>
와 연관이 있어서 여기서 소개한다.
int vfprintf(FILE * restrict stream, const char * restrict format, va_list arg);
int vprintf(const char * restrict format, va_list arg);
int vsnprintf(char * restrict s, size_t n, const char * restrict format, va_list arg);
int vsprintf(char * restrict s, const char * restrict format, va_list arg);
function like macro에서 variable list argument를 다룬 적이 있는데 거기서 다루는 것과 비슷한 방식이다.
고정된 argument를 받고, 마지막에 va_list
type의 값을 통째로 넘겨받는다.
그래서 주로 variable list argument를 가지는 함수 내부에서 쓰인다.
위 함수를 사용할때 va_start
와 va_end
사이에 위치시켜야한다.
각각 fprintf
, printf
, snprintf
, sprintf
와 연관된다.
v...scanf
Functions (<stdio.h>
)<stdio.h>
에 있는 함수들이지만, <stdarg.h>
와 연관이 있어서 여기서 소개한다.
int vfscanf(FILE * restrict stream, const char * restrict format, va_list arg);
int vscanf(const char * restrict format, va_list arg);
int vsscanf(const char * restrict s, const char * restrict format, va_list arg);
각각 fscanf
, scanf
, sscanf
와 대응된다.
사용법은 v...printf
와 같다.
<stdlib.h>
HeaderGeneral Utilities
다른 library에 들어가지 않는 함수들을 모아놓는 header 역할이다.
8개 group으로 분류된다.
3, 7, 8은 앞에서 다뤘으니 제외하겠다.
숫자를 character form으로 가지고 있는 string을 numeric value로 변환하는 함수들
#1
int atoi(const char *nptr);
long int atol(const char *nptr);
double atof(const char *nptr);
#2
long int strtol(const char * restrict nptr, char ** restrict endptr, int base);
unsigned long int strtoul(const char * restrict nptr, char ** restrict endptr, int base);
double strtod(const char * restrict nptr, char ** restrict endptr);
#3
long long int atoll(const char *nptr);
float strtof(const char * restrict nptr, char ** restrict endptr);
long double strtold(const char * restrict nptr, char ** restrict endptr);
long long int strtoll(const char * restrict nptr, char ** restrict endptr, int base);
unsigned long long int strtoull(const char * restrict nptr, char ** restrict endptr, int base);
모두 다 매우 비슷한 방식으로 작동한다.
nptr
로 받은 string에서 white-space는 skip하고, 연속되는 character는 숫자로 간주한다.
part of a number가 될 수 없는 숫자에서 멈추고,
해당 numeric value를 반환한다.
conversion을 할 수 없으면 0을 반환한다.(빈 string이거나, part of a number가 없거나)
atof
, atoi
, atol
은 예전 함수이다.
int
, long int
, double
로 변환하지만,
실패(값이 너무 크거나)했을때 알 수 있는 방법이 없다.
그리고 string내에서 얼마나 진행됐는지도 알 수 없다.
strtol
, strtoul
, strtod
는 C89 함수이다.
endptr
을 통해 어디서 conversion이 멈췄는지 알 수 있다. null character를 가리키는지 확인해서 string이 전체다 진행됐는지 알 수 있다.
(진행상태 딱히 관심없으면 두번째 argument로 NULL
pointer 넘기면 됨)
그리고 base
argument를 통해 변환할 숫자의 기수를 명시할 수 있다.
함수의 return type의 범위를 벗어나는 값이 나오면 errno
에 ERANGE
를 저장한다.
그리고 strtol
은 LONG_MIN
or LONG_MAX
를, strtoul
은 ULONG_MAX
, strtod
는 plus or minus HUGE_VAL
을 반환한다.
atoll
, strtof
, strtoll
, strtoull
은 C99 함수이다.
atoll
은 atol
,
strtof
, strtold
은 strtod
,
strtoll
은 strtol
,
strtoull
은 strtoul
,
에 각각 대응되며, return 값만 다르다.
추가로 C99부턴 strtof
, strtod
, strtold
에 16진수 floating point number와 infinity, NaN이 올 수 있다.
int rand(void);
void srand(unsigned int seed);
rand
는 호출할때마다 0
~RAND_MAX
(<stdlib.h>
) 사이의 값을 반환한다.
"seed" value에 의해 생성된 값으로, 엄밀히 말하자면 random은 아니다.
srand
는 rand
에 seed 값 제공한다. 우리가 seed 값을 세팅할 수 있다.
srand
호출 전에 rand
가 호출된다면 seed 값은 1
로 가정한다.
각 seed는 particular sequence of pseudo-random numbers를 정한다.
따라서 같은 seed를 사용하면 매번 같은 sequence가 나오게 된다.
이게 유용할수도 있지만, random 결과를 원한다면,
time
함수를 사용해 실행할때마다 다른 값을 넣어줄 수 있다.
(자세한 예시는 section 10.2 guess.c와 guess2.c 참고)
여기 함수들은
(1)normally or abnormally 하게 종료하고 OS에 status code를 반환하거나
(2)user's environment에서 정보를 fetch하거나
(3)OS commands 실행하는
simple interface to the OS를 제공한다.
void exit(int status);
exit(n);
== return n;
차이점은 main함수가 아니어도 프로그램을 종료할 수 있다. (예외상황은 아래 Q&A)
EXIT_SUCCESS
, EXIT_FAILURE
도 추가로 정의하고있다.
exit
의 또 다른 portable argument는 0
이다.(EXIT_SUCCESS와 같은 값)
다른 status code를 반환해도 legal이긴하지만 다른 OS에서 portable할진 모른다.
int atexit(void (*func)(void));
함수가 종료될때 몇가지 final actions이 수행된다.
output buffers의 unwritten data를 flush하고, 열려있는 stream을 닫고, 임시 파일을 지운다.
이런 action을 우리가 atexit
함수를 통해 등록할 수 있다.
함수를 등록하는데, 그 함수 이름이 cleanup
이라면 atexit(cleanup);
으로 할 수 있다.
이후에 프로그램이 return
이나 exit
을 통해 정상적으로 종료될때 등록된 함수가 호출된다.
(2개 이상이 등록되면 둘은 거꾸로 호출된다.)
void _Exit(int status);
exit
와 비슷하지만,
atexit
로 등록된 함수들이 호출되지 않고, signal
함수에 전달해뒀던 signal handler도 호출되지 않는다.
추가로, output buffers의 unwritten data를 flush하고, 열려있는 stream을 닫고, 임시 파일을 지우는 것도 implementation-defined이다.
(꼭 할 필요 X)
void abort(void);
exit
와 비슷하지만,
abnormal program termination이 된다.
따라서 implementation에서 정의하는 unsuccessful termination status code를 반환한다.
_Exit
과 마찬가지로,
atexit
로 등록된 함수들이 호출되지 않고, signal
함수에 전달해뒀던 signal handler도 호출되지 않는다.
output buffers의 unwritten data를 flush하고, 열려있는 stream을 닫고, 임시 파일을 지우는 것도 implementation-defined이다.
char *getenv(const char *name);
많은 OS는 user의 characeteristics를 설명하는 set of strings인 "environment"를 제공한다.
이 string은 프로그램이 시작될때 검색할 path나 user의 terminal type 같은 정보를 포함한다.
만약 path 정보를 얻고 싶다면
char *p = getenv("PATH");
라고 할 수 있다.
(string 내용은 바뀔 수 있음에 주의하자)
int system(const char *string);
프로그램 내에서 다른 프로그램을 실행하도록 해준다.(아마 OS command로.. 명확히 기술된건 아닌가봄)
argument는 command가 포함된 string이어야한다.
예를들어 UNIX에서 현재 directory의 file list를 구하고싶으면,
system("ls >myfiles")
라고 할 수 있다. (output redirection)
return 값은 implementation defined이지만,
대게 우리가 실행시킨 프로그램의 termination status code가 return 값이다.
NULL
pointer가 argument면, command processor이 사용가능할 경우 nonzero value를 return한다.
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
정렬된 array에서 특정 값(key)를 찾는다.
첫번째 인자로 key값을 가리키는 pointer, 두번째 인자로 array, 세번째는 array elements 개수, 네번째는 각 elements size, 다섯번째는 qsort
에서처럼 comprison 함수의 포인터를 넘겨주면 된다.
(key와 elements를 비교하기 위한 함수)
key와 매칭되는 element의 pointer를 반환하고, 그게 없다면 NULL
pointer를 반환한다.
C standard에서 요구하진 않지만, 보통 binary search algorithm을 사용한다.
중간의 값을 비교하며 가기 때문에 꽤 빠르다. 1,000,000개의 elements에서도 최대 20번이면 key를 찾을 수 있다.
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Section 17.7에서 다 얘기함.
bsearch
함수에선 sorted array 밖에 못쓰니까, 그 전에 qsort
로 정렬할 수 있음.
int abs(int j);
long int labs(long int j);
long long int llabs(long long int j);
div_t div(int numer, int denom);
ldiv_t ldiv(long int numer, long int denom);
lldiv_t lldiv(long long int numer, long long int denom);
*abs
함수는 절댓값(absolute value)를 구하는 함수이고,
*div
함수는 1번째 argument를 2번째 argument로 나누고, div_t
structure를 반환한다.
div_t
structure는 quot
(quotient) member와 rem
(remainder) member를 포함한다.(ldiv_t
, lldiv_t
도 같음.)
<time.h>
HeaderDate and Time
PASS. 필요할때 찾아보자.
time manipulation 함수, time conversion 함수 등 제공한다.
string을 number로 바꾸는건 엄청 많은데 왜 반대로 하는건 없나?
itoa
함수 같은게 있긴 한데, 표준도 아니고 해서 쓰기 좀 그렇다.
반대로 변환하고 싶으면 sprintf
로 변환하면 된다.
그렇게 생각하니까 sscanf로도 string->number 변환이 되긴하겠네..
strtod
함수에서 string argument로 16진수랑 infinity, NaN을 허용한다고 했는데 얘네 format이 어떻게 되나?
16진수의 경우 0x
, 0X
를 시작으로 하고 뒤에 decimal point를 포함할 수도 있다.
infinity는 INF
나 INFINITY
string이 올 수 있다.(any or all of the letters may be lower case)
NaN은 NAN
string으로 표현된다.(마찬가지로 case 무시)
혹은 뒤에 괄호가 붙을 수도 있는데, 괄호는 비었거나 sequence of characters가 온다.(문자, 숫자, underscore로 구성)
characters는 NaN 값의 binary representation을 명시하기위해 사용되는데, 정확한 의미는 Implementation defined이다.
같은 종류의 character sequence는 nan
함수에도 사용될 수 있다.
(IEEE 754에서 NaN은 지수부에 255, 유효숫자에 0이 아닌 값을 저장한다. 그래서 그 유효숫자에 저장하는 값을 sequence of character에서 지정해주는건가.. 싶네)
(p.609 nan
함수랑 같이봐야 좀 이해됨)
exit(n)
이 보통은 return n;
과 같다고했는데, 예외 상황이 있나?
return
은 automatic storage duration을 가진 경우 main이 종료될때 같이 없어지지만,
exit
는 아니다.
atexit
로 등록된 함수가 그런 변수에 접근해야된다면 return
을 사용한 경우 제대로 작동하지 않을 수 있지만 exit
를 사용하면 잘 작동한다.
(setvbuf
로 지역변수를 buffer로 지정했는데, output buffer 닫아야되거나.. 해도 문제가 생길 수 있음)
또 C99에선 implementation이 명확하게 main의 return type이 달라도 된다고 허용하는 경우가 있는데 그런 경우도 예외 상황이다.
abort
와 SIGABRT
의 관계는?
abort
는 SIGABRT
signal을 발생시킨다.
아무 handler도 없으면 terminates abnormally
handler가 설정돼있으면, 호출하고 반환되고나서 terminates abnormally
handler가 반환하지 않으면(longjmp
등으로 인해) 프로그램은 종료되지 않는다.