오늘은 ctime 함수에 대해서 알아봅시다. 이것은, time_t형을 우리가 알아보기 쉬운 형식인, 예를 들어서 2019-8-25 SUN 11:41:15 형식으로 바꾸는 함수입니다. 물론, 형식을 지정하는 함수도 있습니다만, 이건 다음에 배워보기로 합시다.

 

 

char *ctime(time_t *tar);

 

 

 사용 방식은 time_t형 tar의 주솟값을 넘겨줍니다. 그러면, 1970년 1월 1일 0:0:0 UTC+0을 기준으로 tar초만큼 경과가 되었을 건데요. 만약에 로컽 타임을 한국 시간으로 설정했다면, 1970년 1월 1일 9:0:0 UTC+9겠네요. 기준 시간이. 그로부터 경과된 시간만큼 지났을 때, 몇 년도 몇 월 몇 일이고, 몇 시 몇 분 몇 초인가를 문자열 형태로 리턴하는 함수입니다.

 

 

 대략적으로 ctime이 하는 역할을 그려보면 다음과 같습니다.

 

 


 그러면 예제 프로그램을 봅시다.

 

 

 먼저 4번째 줄에서, now에 time(NULL)값을 넣고 있어요. 제 리눅스 시간대는 서울로 설정이 되어 있고요. 그리고 현재 시간은 11:33:26이였습니다. 그러면 1970년 1월 1일 오전 9시 0분 0초를 기준으로, 2019년 8월 25일 11:33:26까지 경과된 시간을 초단위로 리턴을 합니다.

 

 이 값을 ctime에 넘겨주면, 현재 시간이 우리가 알아보기 쉽게 표시가 됩니다.

 

 

 이렇게 말입니다. 그리 어렵지 않네요.

 

 


 그런데 이 함수 또한 _r이 붙어 있는 버전이 있는 걸로 보아서는 Thread safe 하지 않을 듯 싶네요. 내부적으로 static이라던지, 전역 변수에다가 값을 써가지고, 공통 버퍼의 주솟값을 리턴을 할 겁니다. 정말 그럴까요? 제 환경에서 실험을 해 보겠습니다.

 

 

 이 프로그램을 보시면, now는 현재, 그리고 prev는 어제를 나타냅니다. 우리가 원하는 바는, res1에는 현재 날짜가, res2에는 어제 날짜인 8월 24일이 출력되기를 원하고 있어요.

 

 

 그런데 실제로는 둘 다 어제 날짜가 출력됩니다. 이는, 내부적으로 ctime이 버퍼를 쓰는데, 이게 전역, 혹은 static의 특성을 가지기 때문에 그렇습니다. 이 상황을 그림으로 그려 보겠습니다.

 

 

 ctime의 리턴값에 해당하는 버퍼는 data 영역에 생성되었습니다. 먼저, ctime(&now)를 호출한 경우 이 버퍼에, 2019-8-25라는 문자열이 저장이 될 겁니다.

 

 

 그리고, buf의 주솟값이 리턴될 거에요. 그 리턴값을 main 함수의 res1이 받고 있어요. 그런가요? 따라서, res1은 data 영역에 있는 buf를 가리킵니다.

 

 

 그런데 ctime(&prev)를 호출하면 또 data 영역에 있는 버퍼가 2019-8-24로 갱신이 될 거에요. 그리고 buf의 주솟값이 리턴이 될 건데요. 결론적으로 res1과 res2가 가리키는 공간이 같아요. 정리를 하면, 공유 데이터 버퍼에, 값을 쓰기 때문에 문제가 발생합니다.

 

 


 그러면 이를 해결하기 위한 방법이 없을까요? 일부 라이브러리에서 제공하는, _r이 붙은 ctime_r 함수는 다음과 같이 사용합니다.

 

 

char *ctime_r(time_t *tar,char *buf);

 

 

 2번째 인자에 buf를 넘겨준다는 것이 중요한데요. 결과값을 공유 데이터가 아니라, buf가 가리키는 영역에 쓴다는 것이 다른 점입니다. 예제 프로그램을 봅시다.

 

 

 보시면 스택 영역에 res1과 res2를 넣었습니다.

 

 

 그러면, 버퍼가 별개의 공간에 생성되었어요. 보면, 8번째 줄에, res1에다가, 2019-8-25에 대한 데이터를 씁니다. 그리고, 10번째 줄에서, res2에다가 2019-8-24에 대한 데이터를 씁니다. 그러면, 10번째 줄까지 수행되고 나면, 메모리는 다음과 같을 거에요.

 

 

 따라서, 오늘 데이터와 어제 데이터가 올바르게 출력됩니다.

 

 

 이 정도만 짚고 넘어가셔도 좋을 듯 싶네요.