Chapter 26. Miscellaneous Library Functions

지환·2022년 3월 27일
0

<stdarg.h>
<stdlib.h>
<time.h>
C89 headers인 위 세가지 헤더에 대해 다룬다.


26.1 The <stdarg.h> Header

Variable Arguments
이 헤더에서 제공하는 함수를 통해 printf 같이 다양한 숫자의 arguments를 다룰 수 있다.

Types (<stdarg.h>)

va_list
위 type 하나만 define 한다.

Functions (<stdarg.h>)

va_start & va_arg & va_end

아래는 모두 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;
}    

자세한 설명은 책 참고

va_copy

얘도 function like macro

void va_copy(va_list dest, va_list src);    //C99

src 값을 dest에 복사한다.
variable arguments list에서 해당 argument의 위치도 같이 저장한다.
원본에 영향을 주지 않고 argument를 processing 할 수 있다.

주의

va_startva_copy는 꼭 va_end와 짝을 이루어야 하며,
va_arg는 그 사이에만 올 수 있다.
모두 같은 함수 내에 있어야 한다.

Variable Argument List를 가진 함수printfscanf처럼 잘못된 argument가 들어오는 것에 민감하다.
몇개의 arguments가 들어올지 모른다는 것도 문제가 되는데,
위 예시에선 n을 입력받음으로써 해결했다.(printf는 format string에 의존해 해결)
NULL pointer는 0을 나타내기때문에 int로 인식할 수 있다. 따라서 (void *) NULL 형식으로 cast해서 넘겨주는게 낫다.(Chapter 17 Q&A 참고)

The 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_startva_end사이에 위치시켜야한다.

각각 fprintf, printf, snprintf, sprintf와 연관된다.

The 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와 같다.


26.2 The <stdlib.h> Header

General Utilities
다른 library에 들어가지 않는 함수들을 모아놓는 header 역할이다.

  1. Numeric conversion functions
  2. Pseudo-random sequence generation functions
  3. Memory-management functions(Chapter 17)
  4. Communication with the environment
  5. Searching and sorting utilities
  6. Integer arithmetic functions
  7. Multibyte/wide-character conversion functions(Section 25.2)
  8. Multibyte/wide-string conversion functions(Section 25.2)

8개 group으로 분류된다.
3, 7, 8은 앞에서 다뤘으니 제외하겠다.

Numeric Conversion Functions

숫자를 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가 없거나)

#1

atof, atoi, atol은 예전 함수이다.

int, long int, double로 변환하지만,
실패(값이 너무 크거나)했을때 알 수 있는 방법이 없다.
그리고 string내에서 얼마나 진행됐는지도 알 수 없다.

#2

strtol, strtoul, strtod는 C89 함수이다.

endptr을 통해 어디서 conversion이 멈췄는지 알 수 있다. null character를 가리키는지 확인해서 string이 전체다 진행됐는지 알 수 있다.
(진행상태 딱히 관심없으면 두번째 argument로 NULL pointer 넘기면 됨)
그리고 base argument를 통해 변환할 숫자의 기수를 명시할 수 있다.

함수의 return type의 범위를 벗어나는 값이 나오면 errnoERANGE를 저장한다.
그리고 strtolLONG_MIN or LONG_MAX를, strtoulULONG_MAX, strtod는 plus or minus HUGE_VAL을 반환한다.

#3

atoll, strtof, strtoll, strtoull은 C99 함수이다.

atollatol,
strtof, strtoldstrtod,
strtollstrtol,
strtoullstrtoul,
에 각각 대응되며, return 값만 다르다.

추가로 C99부턴 strtof, strtod, strtold에 16진수 floating point number와 infinity, NaN이 올 수 있다.

Pseudo-Random Sequence Generation Functions

int rand(void);

void srand(unsigned int seed);

rand호출할때마다 0~RAND_MAX(<stdlib.h>) 사이의 값을 반환한다.
"seed" value에 의해 생성된 값으로, 엄밀히 말하자면 random은 아니다.

srandrand에 seed 값 제공한다. 우리가 seed 값을 세팅할 수 있다.
srand호출 전에 rand가 호출된다면 seed 값은 1로 가정한다.

각 seed는 particular sequence of pseudo-random numbers를 정한다.
따라서 같은 seed를 사용하면 매번 같은 sequence가 나오게 된다.
이게 유용할수도 있지만, random 결과를 원한다면,
time 함수를 사용해 실행할때마다 다른 값을 넣어줄 수 있다.
(자세한 예시는 section 10.2 guess.c와 guess2.c 참고)

Communication with the Environment

여기 함수들은
(1)normally or abnormally 하게 종료하고 OS에 status code를 반환하거나
(2)user's environment에서 정보를 fetch하거나
(3)OS commands 실행하는
simple interface to the OS를 제공한다.

exit

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할진 모른다.

atexit

int atexit(void (*func)(void));

함수가 종료될때 몇가지 final actions이 수행된다.
output buffers의 unwritten data를 flush하고, 열려있는 stream을 닫고, 임시 파일을 지운다.

이런 action을 우리가 atexit 함수를 통해 등록할 수 있다.
함수를 등록하는데, 그 함수 이름이 cleanup이라면 atexit(cleanup);으로 할 수 있다.

이후에 프로그램이 return이나 exit을 통해 정상적으로 종료될때 등록된 함수가 호출된다.
(2개 이상이 등록되면 둘은 거꾸로 호출된다.)

_Exit

void _Exit(int status);

exit와 비슷하지만,
atexit로 등록된 함수들이 호출되지 않고, signal 함수에 전달해뒀던 signal handler도 호출되지 않는다.

추가로, output buffers의 unwritten data를 flush하고, 열려있는 stream을 닫고, 임시 파일을 지우는 것도 implementation-defined이다.
(꼭 할 필요 X)

abort

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이다.

getenv

char *getenv(const char *name);

많은 OS는 user의 characeteristics를 설명하는 set of strings인 "environment"를 제공한다.
이 string은 프로그램이 시작될때 검색할 path나 user의 terminal type 같은 정보를 포함한다.

만약 path 정보를 얻고 싶다면
char *p = getenv("PATH"); 라고 할 수 있다.
(string 내용은 바뀔 수 있음에 주의하자)

system

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한다.

Searching and Sorting Utilities

bsearch

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를 찾을 수 있다.

qsort

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Section 17.7에서 다 얘기함.
bsearch 함수에선 sorted array 밖에 못쓰니까, 그 전에 qsort로 정렬할 수 있음.

Integer Arithmetic Functions

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도 같음.)


26.3 The <time.h> Header

Date and Time

PASS. 필요할때 찾아보자.

time manipulation 함수, time conversion 함수 등 제공한다.


Q&A

string을 number로 바꾸는건 엄청 많은데 왜 반대로 하는건 없나?
itoa 함수 같은게 있긴 한데, 표준도 아니고 해서 쓰기 좀 그렇다.
반대로 변환하고 싶으면 sprintf로 변환하면 된다.

그렇게 생각하니까 sscanf로도 string->number 변환이 되긴하겠네..

strtod 함수에서 string argument로 16진수랑 infinity, NaN을 허용한다고 했는데 얘네 format이 어떻게 되나?
16진수의 경우 0x, 0X를 시작으로 하고 뒤에 decimal point를 포함할 수도 있다.

infinity는 INFINFINITY 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이 달라도 된다고 허용하는 경우가 있는데 그런 경우도 예외 상황이다.

abortSIGABRT의 관계는?
abortSIGABRT signal을 발생시킨다.
아무 handler도 없으면 terminates abnormally
handler가 설정돼있으면, 호출하고 반환되고나서 terminates abnormally
handler가 반환하지 않으면(longjmp 등으로 인해) 프로그램은 종료되지 않는다.

0개의 댓글