[C] C언어 공부하기 8. 표준 함수

2020. 1. 2. 16:47C,C++

출처 : http://www.soen.kr/

 

SoEn:소프트웨어 공학 연구소

 

www.soen.kr

 

8-1 수학 함수

8-1-가. 표준 함수

C컴파일러는 많은 수의 표준 함수들을 제공한다. 

실력이 되고 시간이 펑펑 남아 돈다면 자신의 필요에 맞게 원하는 기능만 가지는 함수를 입맛에 맞게 만들어 쓰는 것도 가능하다. 그러나 아무래도 직접 만든 함수는 성능이 떨어질 것이고 충분한 테스트를 거치지 않았기 때문에 어떤 상황에서라도 잘 동작할 수 있는 신뢰성을 갖추기 어렵다. 

그래서 컴파일러 제작사들은 자주 사용되는 공통적인 함수들을 미리 만들어서 컴파일러와 함께 배포하는데 이를 표준 함수라고 하며 표준 함수들의 집합을 런타임 라이브러리(CRT)라고 부른다. 그래서 우리는 원하는 기능을 제공하는 표준 함수를 골라 호출하기만 하면 된다. 이 얼마나 고마운 일인가?

 

8-1-나. 삼각 함수

수학 함수는 수학적인 계산을 하는 함수들이다. 수학 함수들의 원형은 모두 math.h에 선언되어 있으므로 이 함수들을 사용하려면 제일 먼저 #include <math.h> 전처리문을 삽입하여 이 헤더 파일을 포함시켜야 한다. 

double sin(double x);

double cos(double x);

double tan(double x);

double asin(double x);

double acos(double x);

double atan(double x);

double sinh(double x);

double cosh(double x);

double tanh(double x);

삼각 함수들이 받아들이는 인수 x는 360분법의 각도가 아니라 호도(라디안)값이다. 1호도는 원주의 길이가 반지름과 같아지는 각도인데 180/3.1416으로 정의되어 있다.

 따라서 각도값으로 호도를 구할 때는 다음 공식을 사용하면 된다.

 

호도=각도*3.1416/180

다음 프로그램은 그래픽 환경에서 실행되는 아날로그 시계이다.

이 시계에서 시간을 표시하는 12개의 작은 원 좌표와 시침, 분침, 초침의 끝 좌표를 계산하려면 sin, cos 같은 삼각 함수가 반드시 필요하다.

사실 프로그래머의 입장에서 삼각 함수가 어떤 값을 어떻게 계산해 내는가는 전혀 중요하지 않으며 언제 어떤 위치에 삼각 함수를 적절하게 사용하는가가 훨씬 더 중요하다.

 

8-1-다. 지수 함수

지수 함수는 거듭승이나 제곱근, 로그 따위의 값을 구하는 함수들이다.

hypot(x,y)는 sqrt(pow(x,2)+pow(y,2))와 동일하며 조금 더 간단하게 쓴다면 sqrt(x*x+y*y)와 같다. 

 

8-1-라. 정수화 함수

정수화 함수는 실수형 데이터에서 정수부만을 취하는, 즉 소수점 이하의 소수부를 잘라 버리는 함수이다. 

double floor( double x );

double ceil( double x );

 

floor는 내림을 하는 함수이고 ceil은 올림을 하는 함수라고 일단 정리할 수 있다.

러나 단순히 내림, 올림으로 이 두 함수의 동작을 정의하는 것은 정확하지 않다.

 floor : 주어진 인수보다 크지 않은 최대 정수

 ceil : 주어진 인수보다 작지 않은 최소 정수

 실생활에서는 소수점을 잘라 버리거나 또는 인접한 정수값을 취하는 것보다 반올림하는 것이 오차도 적고 가장 일반적으로 애용되는 방법이다. 그러나 C 언어는 반올림 함수를 따로 제공하지 않는다. 왜 반올림 함수가 없는가 하면 굳이 이런 함수를 제공하지 않아도 올림, 내림 함수를 응용하면 반올림을 할 수 있을 뿐만 아니라 응용 방식에 따라 반올림 방식을 자유롭게 선택할 수도 있기 때문이다.

 

실수 x 반올림한  = floor(x+0.5)

 

8-1-마. 절대값 함수

 

int abs(int n);

long labs(long n);

double fabs(double x);

 

위에서부터 순서대로 정수형, long형, 실수형에 대한 절대값을 구한다.

 타입에 따라 다른 함수를 호출하는 것보다는 오히려 삼항 조건 연산자를 사용하는 것이 속도상으로 보나 프로그램 크기로 보나 유리하다. 자주 사용한다면 다음과 같은 매크로를 만들어 쓰는 것도 좋다.

 

#define MyAbs(a) (((a)>0) ? (a):-(a)))

 

지금 여기서 abs 함수를 응용해서 범위 점검을 쉽게 하는 방법이 있다는 것을 소개하고자 하는 것은 아니다. abs같은 아메바 수준의 간단한 함수라도 적절한 위치에 제대로 응용하면 몇 백줄의 코드를 대신하는 역할을 할 수도 있다는 것을 강조하고 싶다. 그러니 지금 당장은 사소해 보이고 별 것 아닌 것 같이 생각되더라도 가벼이 보지 말고 최소한 이런 것도 있었다는 것은 알아 두도록 하자.

 

8-2. 난수 함수

8-2-가. 표준 난수 함수

난수(Random Number)란 무작위로 만들어지는 알 수 없는 값이다. 

 

int rand(void);

void srand(unsigned int seed);

 

rand 함수는 0~RAND_MAX 범위의 수 중에서 무작위로 한 수를 생성해 낸다. RAND_MAX는 컴파일러에 따라 다르지만 일반적으로 32767(0x7ffff)로 정의되어 있다.

 rand 함수는 일정한 규칙에 따라 난수를 생성하는데 규칙이 항상 같기 때문에 난수가 생성되는 순서도 항상 같을 수밖에 없다. 이래서는 제대로 된 난수라고 할 수가 없다.

그래서 난수 생성 루틴의 규칙에 변화를 줄 수 있는 srand라는 함수가 필요하다. srand는 난수 발생기에 난수를 발생시키는 시작점(seed)를 제공하며 난수 발생기는 이 시작점을 기준으로 하여 난수를 발생시킨다. 

그러나 이렇게 하더라도 시작점이 동일하면 생성되는 난수에는 일정한 규칙이 존재할 수밖에 없다. 완전한 난수를 만들기 위해서는 난수 생성기에게 전달되는 시작점 또한 예측 불가능한 난수여야 하는 것이다. 정확한 계산을 하는 기계인 컴퓨터가 완전히 예측 불가능한 난수를 만들어 내는 것은 무척 어려운 일이다. 난수 생성기에게 줄 시작점이 난수여야 한다니 참 곤란하다.

다행스럽게도 컴퓨터에는 난수 발생기의 시작점으로 쓸 수 있는 진짜 난수가 하나 있는데 바로 시간이다. 난수 발생기가 실행될 시점의 시간은 예측할 수 없기 때문에 시간값을 시작점으로 사용한다면 완전한 난수를 만들 수 있다. 

srand((unsigned)time(NULL));

time 함수는 현재 시간을 나타내는 정수값을 리턴하는데 이 값을 시작점으로 사용하면 프로그램이 실행될 때마다 완전히 다른 난수를 만들어낼 수 있다. 

rand 함수가 리턴하는 난수를 일정한 범위안의 수로 바꿀 때는 나머지 연산자 %를 사용한다.

 

rand() % 48       // 0~47 사이의 난수

rand() % 6         // 0~5 사이의 난수

 

이 함수들은 쓰기에 불편하기 때문에 직접 사용하지 않으며 다음과 같이 정의되어 있는 매크로 함수를 대신 사용한다.

 

#define randomize() srand((unsigned)time(NULL))

#define random(n) (rand() % (n))

randomize 함수는 현재 시간을 사용하여 난수 발생기를 초기화하며 random 함수는 인수로 전달된 n사이의 난수를 발생시킨다. rand, srand 함수보다 훨씬 더 직관적이고 원형이 간단하기 때문에 아주 옛날부터 난수 생성을 위해 이 두 매크로 함수를 사용하는 것이 정석이었다. 그래서 볼랜드사의 터보 C, 볼랜드 C 계열 컴파일러는 이 두 함수를 stdlib.h 헤더 파일에 정의해 놓았다.

 

8-2-나. 난수의 생성

1) 0~n사이의 난수는 random(n)으로 생성한다. 

2)  random 함수가 만들어내는 난수의 최소값은 항상 0으로 고정되어 있다. random 함수로 생성한 난수에 상수를 더하면 난수의 범위가 평행이동된다.

3) 난수 사이의 간격은 난수를 구한 후 곱을 사용한다. 100미만의 짝수중 하나를 구하고자 한다면 random(100/2)*2

4) 실수 난수가 필요하면 먼저 충분한 크기의 정수 난수를 구하고 필요한 유효자리수만큼 10의 거듭승으로 나눈다. 

random(100)/10.0

5) 분리된 범위의 난수도 원한다면 생성할 수 있다. 다음 예는 0~4, 14~18 사이의 두 범위에 있는 수 중 하나를 골라준다.

(random(5)+5)*(random(2)==0 ? 1:-1)+9

6) 전혀 연관성이 없는 수들 중 하나를 난수로 선택할 수도 있다. 예를 들어 3, 7, 12, 15 중 하나를 선택하고 싶다고 한다면 다음과 같이 하면 된다.

 

int i;

do {

     i=random(16);

} while (i!=3 && i!=7 && i!=12 && i!=15);

 

원하는 난수가 나올 때까지 루프를 계속 돌리기만 하면 된다.

 

8-3. 시간 함수

 

8-3-가. time

컴퓨터안에는 시계가 내장되어 있어 항상 정확한 시간을 유지하고 있는데 프로그램에서 시간을 필요로 할 경우 시간 함수로 이 값을 조사할 수 있다.

time_t time( time_t *timer );

char *ctime( const time_t *timer );

time 함수는 1970년 1월 1일 자정 이후 경과한 초를 조사하는데 리턴 타입인 time_t형은 시스템에 따라 달라지며 윈도우즈에서는 4바이트 정수(typedef long time_t;)로 정의되어 있다. 

다음 두 코드는 동일하다.

time_t now

now=time(NULL);

time_t now

time(&now);

이 함수는 최대 2038년 1월 18일까지의 날짜를 표현할 수 있으며 64비트 버전인 _time64 함수는 3000년 12월 31일까지 표현 가능하다.
ctime 함수는 time_t형의 경과초를 출력하기 편리한 문자열 형태로 바꾸며 UTC로 된 시간을 지역 설정에 맞게 조정해 주기도 한다. 

변환된 문자열은 26문자 길이로 되어 있으며 끝에 개행 문자가 있어 printf 등의 함수로 곧바로 출력할 수 있다. 

다음 예제는 현재 시간을 time 함수로 조사한 후 ctime 함수로 문자열로 변환하고 그 결과를 printf로 출력한다.

#include <Turboc.h>

 

void main()

{

     time_t t;

 

     time(&t);

     printf("현재 시간은 %s입니다.\n",ctime(&t));;

}

다음 두 함수는 날짜와 시간을 문자열 형태로 바로 구하는 좀 더 간단한 함수이다.

 

char *_strdate(char *datestr);

char *_strtime(char *timestr);

 

_strdate는 날짜를 MM/DD/YY 포맷으로 구해 datestr 버퍼에 복사하며 _strtime은 시간을 HH:MM:SS 포맷으로 구해 timestr 버퍼에 복사하는데 이 함수가 구해주는 시간은 24시간제이다. 

#include <Turboc.h>

 

void main()

{

     char Date[10];

     char Time[10];

 

     _strdate(Date);

     _strtime(Time);

     printf("날짜 : %s, 시간 : %s\n",Date,Time);

}

 

8-3-나. 시간 구조체

time 함수를 사용하면 현재 시간을 쉽게 구할 수 있고 ctime을 사용하면 이 시간을 문자열로도 바꿀 수 있지만 포맷팅을 마음대로 할 수 없어 무척 불편하다.

다음 함수들은 time_t형의 값을 tm 구조체로 변환한다.

 

struct tm *gmtime(const time_t *timer);

struct tm *localtime(const time_t *timer);

time_t mktime(struct tm *timeptr);

gmtime, localtime 함수는 둘 다 time_t형의 값을 tm 구조체로 변환하는데 gmtime은 세계 표준시로 변환하며 localtime은 지역시간으로 변환한다

. tm 구조체는 time.h 헤더 파일에 다음과 같이 선언되어 있다.

 

struct tm {

        int tm_sec;     /* seconds after the minute - [0,59] */

        int tm_min;     /* minutes after the hour - [0,59] */

        int tm_hour;    /* hours since midnight - [0,23] */

        int tm_mday;    /* day of the month - [1,31] */

        int tm_mon;     /* months since January - [0,11] */

        int tm_year;    /* years since 1900 */

        int tm_wday;    /* days since Sunday - [0,6] */

        int tm_yday;    /* days since January 1 - [0,365] */

        int tm_isdst;   /* daylight savings time flag */

};

 

asctime 함수는 tm 구조체를 문자열로 바꾸는데 ctime 함수와 마찬가지로 출력 결과가 영어로 되어 있어 한글 환경에는 실용성이 없고 개행 문자도 포함되어 있다.

 

char *asctime(const struct tm *timeptr);

size_t strftime(char *strDest, size_t maxsize, const char *format, const struct tm *timeptr);

#include <Turboc.h>

 

void main()

{

     time_t t;

     char Format[128];

 

     time(&t);

     strftime(Format,128,"%Y %B %d %A %I:%M:%S %p",localtime(&t));

     puts(Format);

}

 

8-3-다. 기타 시간 함수

 다음 함수는 프로그램이 실행을 시작한 후의 경과된 시간(Process Time)을 조사한다.

 

clock_t clock( void );       

 

clock_t 타입은 long형으로 정의되어 있으며 이 함수가 조사한 값을 CLOCKS_PER_SEC으로 나누면 프로그램 실행 후의 경과 초를 알 수 있다. 이 값은 시스템에 따라 다른데 윈도우즈에서는 1000으로 정의되어 있다.

다음 함수는 두 시간값의 차를 구해준다.

double difftime(time_t timer1, time_t timer0);

'C,C++' 카테고리의 다른 글

[C] C언어 공부하기 10. 포인터  (0) 2020.01.06
[C] C언어 공부하기 9. 배열  (0) 2020.01.02
[C] C언어 공부하기 7.지역변수  (0) 2020.01.02
[C] C언어 공부하기 6. 함수  (0) 2020.01.02
[C] C언어 공부하기 5.연산자  (0) 2020.01.02