12-2.수치와 문자열

12-2-가.정수와 문자열

수치와 문자열은 데이터 타입이 다르며 따라서 메모리에 기억되는 모양도 상당히 다르다. 다음 두 변수를 보자.

 

int num=123;

char str[]="123";

 

두 변수 모두 123으로 초기화되었는데 정수형 변수 num은 수치값 123을 가지고 있고 문자 배열 str은 문자열 "123"을 가지고 있다. 이 두 값은 외형적인 모양이 비슷해 보이지만 내부적으로는 완전히 다른 값이다. num의 값 123은 백이십삼이라는 수치이며 이 값은 122보다 하나 더 많고 124보다는 하나 더 작다는 성질을 가지고 있다. 반면 문자열 str의 값 "123"은 문자 '1'과 문자 '2'와 문자 '3'이 연속적으로 배치되어 있다 뿐이지 산술적인 의미를 가지지 않는다.

문자열과 수치는 내부적인 모양이 완전히 다르기 때문에 상호 대입하거나 연산할 수 없다. 예를 들어 str=123이라고 대입한다거나 a=num+str과 같이 덧셈을 할 수 없는 것이다. 또한 각각은 타입이 맞는 위치에만 사용할 수 있다. num이 123의 값을 가진다고 해서 puts(num)을 호출하면 123이 출력되는 것이 아니며 str이 "123"일 때 delay(str)이라고 호출한다고 해서 0.123초 동안 실행이 중지되는 것도 아니다.

콘솔 환경에서는 이런 두 형식의 차이점을 어느 정도 완충해주는 장치가 있다. 화면으로 직접 출력 가능한 형식은 문자열뿐인데 printf 함수는 %d, %f 서식에 의해 정수나 실수를 문자열로 바꾸어 출력한다. 키보드는 원칙적으로 문자열을 입력하는 장비이지만 입력된 문자열이 scanf 함수에 의해 수치값으로 바뀌어 변수에 대입된다.

그러나 그래픽 환경(GUI)에서는 이런 장치가 제공되지 않는다. 윈도우즈의 표준 출력 함수인 TextOut은 오로지 문자열만 출력할 수 있고 에디트 컨트롤이 입력받는 값도 원칙적으로 문자열뿐이다. 이런 경우에는 수치와 문자열을 원하는 형태로 직접 바꾼 후 사용해야 한다. 이 절에서는 수치, 그러니까 정수와 실수를 문자열로 상호 변환하는 방법에 대해 알아볼 것이다.

다음 함수들은 정수를 문자열로 변환한다. 정수의 타입에 따라 세 개의 함수가 준비되어 있는데 32비트 환경에서는 long형이 int와 같기 때문에 ltoa 함수는 itoa 함수와 동일하다. 주로 itoa 함수를 많이 사용하며 부호없는 정수일 경우에만 ultoa 함수를 사용한다.

 

char *itoa(int value, char *string, int radix);

char *ltoa(long value, char *string, int radix);

char *ultoa(unsigned long value, char *string, int radix);

 

itoa 함수는 Integer to Ascii의 준말이며 말 뜻 그대로 정수를 문자열로 변환한다. 첫 번째 인수에 바꾸고자 하는 정수값을 주고 두 번째 인수에 변환한 문자열을 저장할 배열을 준다. 세 번째 인수 radix는 변환시 사용할 진법을 지정하는데 10진법으로 변환할 경우 10이라고 쓰면 된다.

 

int num=123;

char str[10];

itoa(num,str,10);

 

이 코드에 의해 str에 "123"이 복사될 것이다. num에 저장된 수치값이 문자열 형태로 바뀌어 str 배열에 저장되었으므로 puts(str)로 이 문자열을 출력하면 num의 값이 화면에 출력된다. itoa 함수도 배열 끝 점검을 하지 않으므로 두 번째 인수(위 코드에서 str)로 충분한 길이의 배열을 제공해야 한다. itoa 함수의 세 번째 인수 radix는 보통 10으로 주는데 진법을 바꾸고 싶을 때는 원하는 진법을 지정할 수 있다. 다음 예제는 12345라는 10진수를 2진법~20진법까지 바꿔가며 출력해 본 것이다.

 

: itoa

#include <Turboc.h>

 

void main(void)

{

     int num=12345;

     int radix;

     char str[32];

 

     for (radix=2;radix<=20;radix++) {

          itoa(num,str,radix);

          printf("%d진법 : %s\n",radix,str);

     }

}

 

radix를 제어 변수로 하여 2~20까지 루프를 돌면서 itoa를 반복적으로 호출했다. 출력 결과는 다음과 같다.

 

2진법 : 11000000111001

3진법 : 121221020

4진법 : 3000321

5진법 : 343340

6진법 : 133053

7진법 : 50664

8진법 : 30071

9진법 : 17836

10진법 : 12345

11진법 : 9303

12진법 : 7189

13진법 : 5808

14진법 : 46db

15진법 : 39d0

16진법 : 3039

17진법 : 28c3

18진법 : 221f

19진법 : 1f3e

20진법 : 1ah5

 

똑같은 수치값 12345라도 진법에 따라 이렇게 다양한 형태로 표현할 수 있다. 단, 각 진법에 따른 출력 결과는 표현 방법이 다를 뿐이지 모두 10진수로 12345라는 값을 나타낸다. radix 인수의 가능한 값은 2~36까지이다. 논리적으로 1진법이라는 것은 존재할 수 없으므로 최소 진법은 2진법부터이며 숫자 표현에 사용할 수 있는 문자가 0~9, a~z까지 총 36개뿐이므로 36진법까지만 지원한다.

다음은 문자열을 정수로 바꾸는 함수에 대해 알아보자. 타입에 따라 많은 함수들이 준비되어 있는데 이 중 atoi 함수가 기본 함수이다. atoi는 물론 Ascii to Integer의 약자이다.

 

int atoi(const char *string);

long atol(const char *string);

long strtol(const char *nptr, char **endptr, int base);

unsigned long strtoul(const char *nptr, char **endptr, int base);

 

문자열 str에 "123"이 저장되어 있을 때 atoi(str)은 수치값 123을 리턴한다. 이 함수는 이미 앞 장에서 main 함수의 인수로 전달된 문자열을 정수로 바꾸기 위해 사용해 본 적이 있다. 명령행에서 입력되는 프로그램의 인수 argv는 문자열 배열이기 때문에 정수를 바로 입력받을 수는 없다. 그래서 정수 형태로 입력된 문자열을 atoi 함수를 사용하여 정수로 바꿔서 사용해야 한다.

atoi 함수의 인수로 전달되는 문자열은 수치값의 형태로 저장되어 있어야 한다. 수치값 표현에 사용할 수 있는 문자는 0~9까지의 아라비아 숫자와 +,- 부호 정도이다. 그러므로 atoi("123"), atoi("-78") 이런 식의 문자열을 전달해야 제대로 변환된다. atoi("aaa")처럼 수치가 아닌 문자열을 전달할 경우 atoi는 변환할 수 없다는 의미로 0을 리턴한다.

만약 수치와 문자열이 섞여 있다면 atoi는 에러를 리턴하는 대신 변환 가능한 부분까지만 변환한다. 예를 들어 atoi("12month")를 호출하면 12가 리턴된다. "12month"는 비록 완전한 수치값 형태의 문자열은 아니지만 최대한 해석 가능한 부분까지는 변환해 주는 것이다. 따라서 숫자와 문자가 섞인 문자열의 경우 별도의 처리없이 부분적인 변환이 가능하다는 편리함이 있지만 정확한 변환이 아니라는 점에서는 주의를 할 필요가 있다.

atol 함수는 문자열에 저장된 수치를 long 형으로 변환하는데 32비트 환경에서는 int와 long이 같은 타입이므로 atoi와 완전히 같은 동작을 한다. strtol 함수는 atoi 함수에 비해 진법을 인식한다는 점에 있어서 조금 더 기능이 많다. atoi는 문자열에 들어 있는 값을 10진수로만 인식하지만 strtol 함수는 세 번째 인수 base로 문자열에 들어 있는 값의 진법을 지정할 수 있다.

만약 base가 10이라면 strtol은 string 인수를 10진법의 값으로 인식하므로 이 경우는 atoi 함수와 같은 동작을 한다. base가 16이라면 string은 16진수이므로 0~9까지의 문자 외에 a~f까지의 문자도 숫자로 인정될 것이다. "10011100"과 같이 이진수 형태로 저장된 문자열도 strtol 함수를 사용하면 정수로 변환할 수 있다.

지정한 base에 사용할 수 없는 문자가 발견될 경우 그 위치에서 변환을 중지한다. 예를 들어 base가 8이면 0~7까지의 숫자만 사용할 수 있는데 문자열에 "2390"이 저장되어 있다면 "23"까지만 변환될 것이다. 이때 변환이 중지된 지점의 번지가 두 번째 인수 endptr에 대입되므로 어디서 중지되었는지를 조사할 수 있다. 만약 에러 검출 기능을 사용하지 않는다면 strtol의 두 번째 인수로 NULL을 지정하면 된다. 다음 예제는 16진수 abcd를 정수로 변환한 후 그 값을 printf로 출력해 보았다.

 

: strtol

#include <Turboc.h>

 

void main(void)

{

     char *endptr;

     char str[]="abcd";

 

     long l=strtol(str,&endptr,16);

     printf("%ld\n",l);

}

 

16진수 abcd는 10진수로 43981이므로 43981이 출력될 것이다. printf의 출력 서식을 %d 대신 %x를 사용하면 abcd가 그대로 출력된다. strtol이 정수로 바꾼 값을 printf의 %x 서식이 다시 16진수 형태의 문자열로 바꾸어 출력하기 때문이다.