코딩/C와 C++

[C++ 기초 강좌] 3. C++ 데이터 형식

드리프트 2021. 3. 11. 18:13
728x170

 

 

하나의 변수가 저장할 수 있는 정보의 종류는 그것의 데이터 형식에 의해서 결정되나. 여러분의 프로그램 안에 있는 모든 데이터와 변수는 어떤 정의된 형식을 가지고 있다. C++는 여러분에게 다양한 표준 테이터 형식을 제공한다. 그러한 형식은 특정한 키워드에 의해서 지정된다. 우리는 이미 정수 변수를 정의하는 int라는 키워드를 알고 있다. 언어의 객체 지향적인 측면에서 여러분은 여러분만의 데이터 형식을 생성할 수 있다. 이것에 관해서는 나중에 살펴볼 것이다. 일단은 C++가 제공하는 기초적인 수치 데이터 형식에 대해서 알아본다.

 


정수 변수

앞에서도 알아보았듯이 정수 변수는 정수 값만을 가지는 변수이다. 야구팀의 선수들의 숫자는 적어도 게임이 시작할 때는 정수이다. 우리는 이미 int 키워드를 사용하여 정수 변수를 선언할 수 있다는 것을 알고 있다. 이러한 정수 변수는 4바이트의 메모리를 차지하는 변수로서 양의 값과 음의 값을 가질 수 있다.

C++에서 short라는 키워드도 정수 변수를 정의한다. 그러나 이 경우 2바이트를 차지한다. short는 short int와 동일한 의미를 가진다.
또한 C++는 다른 종류의 정수 형식인 long을 제공하는데, 이것도 long int라고 쓸 수 있다. 이 경우, 우리는 다음과 같은 문장을 쓸 수 있다.

long bigNumber = 1000000L, largeValue = 0L;


여기서 우리는 초기값으로 1000000과 0을 가지는 bigNumber와 largeNumber라는 변수를 선언하였다. 값의 끝부분에 붙어 있는 L이라는 문자는 그것들이 long 정수라는 것을 나타낸다. 또한, 여러분은 동일한 목적으로 1을 사용할 수 있다. 그러나 소문자 l은 숫자 1과 혼동될 수 있다는 결점이 있다.


우리는 프로그램에서 커다란 숫자를 표시할 때 컴마를 사용하지 않았다.


long으로 선언된 정수 변수는 4바이트를 차지하고, -2,147,483,648에서 2,147,483,647까지의 값을 가질 수 있다.


다른 C++ 컴파일러에서는 long과 long int가 int와 동일하지 않을 수도 있다. 그러므로 여러분의 프로그램을 다른 환경에서도 컴파일해야 한다면 long과 int가 같다는 생각은 하지 않아야 한다. 정말로 이식하기 쉬운 코드를 작성하려면 심지어 int가 4바이트를 차지한다는 생각을 버려야 한다.

 


char 데이터 형식

char 테이터 형식은 두 가지의 목적으로 사용된다. 이 형식은 여러분이 정수 또는 하나의 ASCII 문자를 저장하는 데 사용할 수 있는 1바이트의 변수를 지정한다.


우리는 다음과 같은 문장으로 char 변수를 선언할 수 있다.

char letter = ‘A’;


이 문장은 letter라는 변수를 선언하고, 그것에게 초기값으로 상수 ‘A’를 할당한다. 이때 우리가 이전에 화면에 나타낼 문자열을 정의할 때 이중 인용 부호를 사용했던 것과는 달리, 하나의 문자를 단일 인용 부호 사이에 지정하였다는 것을 명심하라, 문자열이라는 것은 char 형식을 가지는 값들로 구성된 일련의 집합으로서 배열이라고 불리는 하나의 엔티티 안으로 묶여 있다. 우리는 다음 강의에서 배열에 대해서, 그리고 문자열이 어떻게 처리되는지에 대해서 살펴볼 것이다.


문자 ‘A’는 ASCII에 의해서 65라는 십진수의 값을 가지므로 다음과 같이 쓸 수도 있다.

char letter = 65;


그래도 이전의 문장과 동일한 결과를 낸다. char 형식의 변수에 저장될 수 있는 정수의 범위는 -128에서 127까지이다.
또한 우리는 16진수 상수를 사용하여 char 변수(그리고 다른 정수 변수)를 초기화할 수 있다. 16진수 숫자는 16진수에 대한 표준 표기법을 사용하여 표시된다. 즉, 0에서 9까지는 보통 숫자로, 그리고 10에서 15까지는 A에서 F(또는, a에서 f)로 표시된다. 또 16진수는 그 앞에 0x(또는 x0)가 붙어서 십진수와 구별된다. 그러므로 동일한 결과를 얻기 위해서는 위의 마지막 문장을 다음과 같이 쓸 수 있다.

char letter = 0x41;


십진 정수 앞에 0을 쓰지 말라, 컴파일러는 그러한 값을 8진수로 인식하게 된다. 그러므로 065라고 쓰인 값은 십진 표기법으로 53과 동일한 값이 된다.

 


정수 형식의 수정자(Modifier)

우리가 방금 살펴보았던 정수 형식(char, int, short 또는 long)을 가지는 변수는 디폴트로 부호가 붙어 있는 값을 포함한다. 즉, 그것들은 우리가 본 바와 같이 양과 음의 값을 저장할 수 있다. 그것은 이러한 형식들이 디폴트로 형식 수정자인 signed를 가지기 때문이다. 그러므로 우리가 프로그램에서 char, int, 뜨는 long을 작성할 때마다 우리는 각각 signed char, signed int, 또는 signed long을 작성하게 되는 것이다.

만약 여러분이 음의 값을 변수에 저장할 필요가 없을 때(예를 들어, 여러분이 7일 동안 자동차로 달린 거리를 기록할 것이라면) 여러분은 변수를 unsigned로서 지정할 수 있다.

unsigned long mileage = 0UL;


여기서 mileage 변수에 저장될 수 있는 최소값은 0이고, 최댓값은 4,294,967,295(즉, 2의 13승 -1)이다. 이것을 signed long의 -2,147,483,648에서 2,147,483,647까지의 값과 비교하라, signed 변수에서 부호를 결정하기 위해서 사용된 비트가 대신 unsigned 변수에서는 수치 값을 나타내는 부분으로 사용되었다. 결과적으로 unsigned 변수는 양의 값으로 더 넓은 범위를 갖게 된다. 그러나 음의 값을 가질 수 없다. U(또는 u)가 어떻게 unsigned 상수에 붙는지를 확인하라. 위의 예에서 우리는 L을 붙여서 그 상수가 long이라는 것을 나타내었다. 여러분은 대문자 또는 소문자 U와 L을 사용할 수 있다. 이때 순서는 중요하지 않다. 그러나 그러한 값을 지정하는 데 있어서 일관된 방법을 사용하는 것이 좋다.

물론, signed와 unsigned는 키워드이다. 그러므로 여러분은 이것들을 변수 이름으로 사용해서는 안된다.

 

부동 소수점 변수

정수가 아닌 값은 부동 수수점 숫자로 저장된다. 부동 수수점 숫자는 112.5 또는 1.125E2(십진수 부분이 E 다음의 숫자에 의해 지정된 멱으로 곱해진다)와 같은 십진수로 표현된다. 그러므로 위의 숫자는 1.125x10의 2승, 즉 112.5이다.

부동 소수점 상수는 소수점 표현 또는 지수 표현, 아니면 그 둘 다를 포함해야 한다. 여러분이 그렇게 쓰지 않으면 그 숫자는 정수로서 인식된다.

여러분은 다음의 문장과 같이 double 키워드를 사용하여 부동 소수점 변수를 지정할 수 있다.

double in_to_mm = 25.4;


double 변수는 8바이트의 메모리를 차지하고 15자리까지의 정확도로 값을 저장한다. 저장되는 값의 범위는 15 자릿수의 정확도보다 훨씬 넓은 범위이다. 즉, 양과 음으로 1.7x10의 -308승에서 1.7x10의 308승까지의 값이다.

만약 여러분이 15 자릿수의 정확도를 필요로 하지 않는다면 double 변수에 의해서 제공되는 대규모 값의 범위를 필요로 하지 않을 것이다. 이때 여러분은 float 키워드를 사용하여 4바이트를 차지하는 부동 소수점 변수를 선언할 수 있다. 예를 들어, 다음과 같은 문장

float pi = 3.14159f;


는 초기값 3.14159를 가지는 pi라는 변수를 정의한다. 상수의 끝부분에 있는 f는 그것이 float 형식이라는 것을 나타낸다. F를 쓰지 않으면 그 상수는 double 형식을 가지게 된다. float로 선언된 변수는 7자리의 정확도를 가지며, 양과 음으로 3.4x10의 -38승에서 3.4x10의 38승의 값을 가진다.

 

논리 변수

논리 변수는 true와 false라는 단지 두 개의 값만을 가질 수 있다. 논리 변수의 형식은 bool로, 불린 대수를 개발했던 조지 부울(George Boole)의 이름을 따라 지어졌다. bool 형식을 가지는 변수는 true 또는 false 값을 가지게 되는 테스트(하나의 값을 다른 값과 비교하는 것과 같은)의 결과를 저장하는 데 사용된다.


여러분은 다음과 같은 문장으로 bool 형식의 변수를 선언할 수 있다.

bool testResult;


물론, 그것을 선언할 때 동시에 초기값을 지정할 수도 있다.

bool colorIsRed = true;


여러분은 true와 false값이 수치 형식을 가지는 변수들과 함께(특히, int 형식을 가지는 변수와 함께) 매우 폭넓게 사용된다는 것을 알게 될 것이다. 이것은 C++에서 bool 형식의 변수가 구현되기 전에는 일반적으로 논리값을 표현하는 데 정수 변수가 사용되었던 데에서 비롯된 구시대의 산물이다. 이 경우에 0 값은 false로, 0이 아닌 값은 true로 인식된다. 

 

특정한 값의 집합을 가지는 변수

때로, 여러분은 레이블로 유용하게 참조될 수 있는 제한된 범위의 값(예를 들어, 요일 또는 달)만을 가지는 변수를 필요로 하는 경우가 있을 것이다. C++ 에는 이러한 상황에 대처할 수 있는 특정한 기능이 있는데, 그것을 목록(enumeration)이라고 한다. 우리가 방금 언급했던 경우 중의 하나를 살펴보도록 하자. 즉, 하나의 변수가 요일에 해당하는 값을 갖는 경우이다. 우리는 그것을 다음과 같이 정의할 수 있다.

enum Week {Mon, Tues, Wed, Thurs, Fri, Sat, Sun} this_week;


위 문장은 Week라고 불리는 목록 형식(enumeration type)과 this_week라는 변수를 선언하는데, 이 변수는 Week라는 목록 형식의 한 경우로서 괄호 사이에서 지정되어 있는 값만을 가질 수 있다. 만약 여러분이 this_week라는 변수에 지정된 값 이외의 다른 어떤 것을 할당하려고 한다면 에러가 발생할 것이다. 괄호 사이에 나타나 있는 심벌 이름을 열거자(enumerator)라 한다. 사실, 자동적으로 요일의 각 이름은 고정된 정수 값을 나타내는 것으로 정의된다. 리스트에 있는 첫 번째 이름인 Mon은 0의 값을 갖고, Tues는 1의 값을 갖는다. 등등…


디폴트로, 각각의 연속적인 열거자는 이전의 열거자보다 더 큰 값을 가진다. 그러나 여러분이 함축적으로 다른 값에서부터 열거자를 시작하고자 한다면 다음과 같이 하면 된다.

enum Week {Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun} this_week;


이렇게 하면 각각의 열거자는 1에서 7까지의 값을 가지게 된다. 심지어 열거자는 특유의 값을 가질 필요가 없다. 여러분은 다음과 같이 Mon과 Tues가 1이라는 값을 가지도록 정의할 수 있다.

enum Week {Mon = 1, Tues = 1, Wed, Thurs, Fri, Sat, Sun} this_week;


this_week라는 변수는 int와 같은 종류의 것이므로 목록 형식을 가지는 다른 모든 변수들과 마찬가지로 4바이트를 차지한다.
목록의 형태를 정의했으므로 여러분은 또 다른 변수도 정의할 수 있다.

enum Week next_week;


위의 문장은 next_week라는 변수를 이전에 지정된 값을 갖는 하나의 목록으로서 정의한다. 여러분은 변수를 선언할 때 enum이라는 키워드를 빼도 된다. 그러므로 위의 문장 대신 다음과 같이 쓸 수 있다.

Week next_week;


여러분이 원한다면 모든 열거자에서 특정한 값들을 할당할 수 있다. 예를 들어, 다음과 같은 목록을 정의할 수 있다.

enum Punctuation {Comma = ‘,’, Exclamation = ‘!’, Question = ‘?’} things;


여기서 우리는 things 변수가 가질 수 있는 값으로서 각 심벌에게 해당되는 숫자값을 정의하였다. ASCII 표를 인터넷으로 찾아본다면 그 심벌들이 각각 십진수로 44, 33, 그리고 63의 값을 가진다는 것을 알게 될 것이다. 여러분도 볼 수 있듯이 할당된 값들은 올림차순으로 될 필요는 없다. 만약 여러분이 모든 값을 명시적으로 지정하지 않았다면 각각의 열거자는 우리의 두 번째 Week 예에서처럼 마지막으로 지정된 값으로부터 1씩 증가하는 값들로 할당될 것이다.

여러분이 나중에 목록 형식의 변수를 정의할 필요가 없다면 여러분은 다음과 같이 목록 형식을 굳이 쓰지 않아도 된다. 예를 들어,

enum {Mon, Tues, Wed, Thurs, Fri, Sat, Sun} thisWeek, nextWeek, lastWeek;


여기서 우리는 Mon에서 Sun까지의 값을 가지는 세 개의 변수를 선언하였다. 목록 형식이 지정되지 않았으므로 우리는 그것을 참조할 수 없다. 이때 여러분은 이 목록에 대하여 다른 변수를 절대로 정의할 수 없다. 왜냐하면, 똑같은 정의를 반복하여 할 수 없기 때문이다. 그렇게 한다는 것은 여러분이 Mon에서 Sun까지의 값을 다시 정의한다는 것을 의미하기 때문에 그것은 불가능하다.

 


여러분만의 데이터 형식 정의하기

typedef 키워드는 여러분만의 데이터 형식 지정자(specifier)를 정의할 수 있도록 해준다. 다음과 같은 선언문에서 여러분은 typedef를 사용하여 BigOnes라는 형식 이름을 표준 long int 형식과 동일한 것으로 정의할 수 있다.

typedef long int BigOnes;


위의 문장은 BigOnes를 long int에 대한 또 다른 형식 지정자로서 정의한다. 그러므로 여러분은 다음과 같이 mynum이라는 변수를 long int 형식으로서 정의할 수 있다.

BigOnes mynum = 0;


위와 같이 선언하는 것과 내장된 형식 이름을 사용하여 선언하는 것의 차이점은 없다. 다음과 같이 해도 된다.

long int mynum = 0;


정확하게 결과는 동일하다. 사실, BigOnes와 같이 여러분만의 형식 지정자를 정의하면 동일한 프로그램 안에서 여러 다른 변수를 선언할 때 그 두 가지의 형식 지정자를 사용할 수 있다. 물론, 그 변수들은 모두 동일한 형식을 가지게 된다.


typede는 단지 기존의 형식에 해당하는 형식만을 정의하기 때문에 이러한 기능이 약간은 불필요한 것으로 생각될 수도 있다. 나중에 우리는 이제까지 보아왔던 것보다 더 복잡한 선언문을 단순화하는 작업에서 이 기능이 매우 유용한 역할을 할 수 있다는 것을 보게 될 것이다. 또한, 완전히 새로운 데이터 형식을 정의하는 기능을 클래스가 제공한다는 사실에 대해서도 살펴볼 것이다.



리터럴

C++에서 모든 종류의 고정된 값을 갖는 것을 리터럴(literal)이라고 한다. 23, 3.141592, 또는 “Samuel Clemens”와 같은 값은 각각 정수 리터럴, 부동 소수점 리터럴, 그리고 문자열 리터럴의 예이다. 종종, 프로그램 안에서 리터럴을 사용해야 할 경우가 생긴다. 예를 들어, 피트 단위를 인치로 변환한다든지, 에러 메시지를 지정하는 경우가 그렇다. 그러나 프로그램에서 꼭 필요한 경우가 아니라면 리터럴을 프로그램에서 명시적으로 사용하는 것은 피해야 한다. 여러분이 2.54라는 값을 사용할 때 반드시 모든 사람이 그 값이 1인치를 센티미터로 표현한 것이라는 사실을 알 것이라는 보장은 없다. 대신에 여러분의 리터럴에 해당되는 고정된 값을 가지고 있는 변수를 선언하는 것이 더 낫다.

 

예를 들어, 여러분은 그 변수의 이름을 inchesToCentimeters로 정할 수도 있다. 그러면 여러분의 코드에서 inchesToCentimeters 변수를 사용할 때 그것의 의미가 매우 분명 해지는 것이다. 우리는 나중에 변수의 값을 고정시키는 방법에 대해 살펴볼 것이다.

 


기본적인 입출력 작업

여기서 우리는 당분간 C++를 배우는 데 충분할 만큼의 C++ 입출력에 대해서만 살펴볼 것이다. 그것은 그리 어렵지 않다(오히려 그 반대이다).

C++의 입출력 메커니즘은 데이터 스트림이라는 개념을 기반으로 한다. 우리는 출력 스트림 안에 데이터를 입력할 수 있고, 입력 스트림으로부터 데이터를 추출할 수 있다. 우리는 이미 화면에 대한 표준 출력 스트림을 cout라고 한다는 사실을 배웠다. 키보드로부터의 입력 스트림은 cin이라고 한다.

 


키보드로부터의 입력

우리는 cin 스트림을 통하여 키보드로부터 입력을 받는다. 이때 >> 스트림에 대한 추출 연산자(extractor operator)를 사용한다. 키보드로부터 두 개의 정수값을 입력받아서 num1과 num2 변수에 저장하려면 다음과 같이 쓴다.

cin >> num1 >> num2;

 

연산자는 데이터가 흐르는 방향을 가리킨다. 이 경우, cin으로부터 두 개의 변수로 차례로 전달된다. 중간에 있는 여백은 건너뛰며, 여러분이 왼쪽에서 오른쪽으로 실행되기 때문이다. Num1 다음에 있는 여백은 무시되고, 여러분이 입력하는 두 번째 값이 num2로 읽혀진다. 그러나 연속되는 값 사이에는 여백이 있어야 한다. 그래야 그것들이 구별될 수 있다. 스트림 입력 작업은 여러분이 <Enter> 키를 누를 때 종결된다. 그리고 프로그램 실행은 그다음 문장으로 이어진다. 물론, 여러분이 틀린 데이터를 입력하면 에러가 발생한다. 그러나 우리는 여러분이 언제가 올바르게 입력했다고 가정한다.

부동 소수점 값도 정수 값과 정확하게 동일한 방법으로 키보드로부터 입력된다. 그리고 물론, 그 두 개를 섞을 수 있다. 스트림 입력 작업은 모든 기본적인 형식의 변수와 데이터를 자동적으로 처리한다. 예를 들어,

int num = 0, num = 0;
double factor = 0.0;
cin >> num1 >> factor >> num2;

 

위 문장의 마지막 줄은 정수를 num1 안으로 읽어 들인다. 그런 다음, 부동 소수점 값을 factor 안으로 읽어 들인다. 마지막으로, 정수를 num2 안으로 읽어 들인다.

 


이스케이프 시퀀스

인용 부호 사이에 문자열을 적을 때 우리는 이스케이프 시퀀스(escape sequence)라는 특별한 문자를 포함시킨다. 이것은 문자열 안에 들어 있지 않으면 표현되지 않는 문자를 문자열 안에 포함시키는 역할을 한다. 하나의 이스케이프 시퀀스는 백 슬래시 문자(\)로 시작한다. 예를 들어, 탭 문자는 \t로 표현한다. 그러므로 다음과 같은 두 가지의 문장

cout << endl<< “This is output.”;
cout << endl << “\tThis is output after a tab.”;

 

은 다음과 같은 출력을 생성한다.

This is output.
	This is output after a tab.


사실, 개행 문자로 endl을 사용하는 대신에 우리는 \n이라는 이스케이프 시퀀스를 각각의 문자열 안에 포함시킬 수 있다. 그러므로 우리는 위의 문장을 다음과 같이 쓸 수 있다.

cout << “\nThis is output.”;
cout << “\n\tThis is output after a tab.”;


다음은 특별히 유용한 몇 가지 이스케이프 시퀀스이다.

 

이스케이프 시퀀스 역할
\a beep음을 울린다
\n 새로운 행을 시작한다
\' 단일 인용 부호
\\ 백슬래시
\b 백스페이스
\t
\" 이중 인용 부호
? 물음표


물론, 여러분이 백 슬래시나 이중 인용 부호를 인용 부호 사이의 문자열 안에 포함시켜서 출력하기를 원한다면 여러분은 이스케이프 시퀀스를 사용하여 그것들을 표현해야 한다. 그렇게 하지 않으면 백 슬래시는 또 다른 이스케이프 시퀀스로 인식되어서 그 이중 인용 부호는 문자열의 끝을 나타내는 역할을 하게 될 것이다.
또한, 여러분은 char 변수에 초기값을 부여할 때 이스케이프 시퀀스에 의해 지정된 문자를 사용할 수 예를 들어,

char Tab = ‘\t’;


이러한 이스케이프 시퀀스는 입출력 작업에 있어서 충분한 기초가 된다.

그리드형