변수
Variable
- 데이터를 저장할 메모리 공간에 프로그래머가 부여하는 이름
변수를 선언하는 위치
- C++에서는 위치에 관계없이 변수를 사용하기 시작한 부분 이전에만 선언을 해주면 된다.
- C나 Pascal의 경우, 사용하는 모든 변수는 함수나 프로시저의 시작위치에 선언해야 한다.
변수를 선언하는 이유
- BASIC과 같은 언어에서는 변수를 따로 선언할 필요 없이, 구문내에서 새로운 키워드는 곧 변수로 인식하고 컴파일을 하는데, 여기서 오타가 발생할 경우에 C++과 같이 오류 메세지를 출력하고 컴파일을 중단하는 것이 아닌, 새로운 변수로 인식하고 컴파일을 진행하게 되어 결국 논리오류를 야기하게 된다.
- 결국 C++을 비롯한 여러 언어에서 변수를 선언하고 사용하는 이유는 잠재적인 버그의 소지를 없애기 위해서이다.
선언 구문 (Declaration Statement)
- 저장될 데이터의 데이터형(Data type)과 할당될 저장소(Memory Storage)의 이름으로 구성됨
Example
int carrots;
// int = Data type
// carrots = 저장소 이름
대입 구문 (Assignment Statement)
- 대입 연산자(Assignment Operator)인 Equal(=)을 이용함
- C와 C++ 모두, 대입 연산자를 연이어 사용할 수 있음
int A, B, C;
C = B = A = 100;
// A에 100이 저장되고, 현재 A의 값(100)이 B에 저장되고, 현재 B의 값(100)이 C에 저장됨
C++에 내장된 데이터형에는 기본형(Fundamental types)과 복합형(Compound types)이 있음
기본형 : 정수형(Integers), 부동 소수점형(Floating point numbers)
복합형 : 기본형을 기초로 하여 만들어진 데이터 타입, 배열(Arrays), 문자열(Strings), 포인터(Pointers), 구조체(Structures)
변수 이름의 규칙
- 변수에는 영문자, 숫자, 언더바(_) 만 사용할 수 있다.
- 숫자는 변수의 첫 글자가 될 수 없다.
- 변수에서 대문자와 소문자는 구별된다.
- C++의 키워드는 변수 이름으로 사용될 수 없다.
- 두 개의 언더바(_)로 시작하는 이름, 언더바와 대문자로 시작하는 이름은, 컴파일러, 리소스가 사용하기로 예약되어 있으므로 사용할 수 없다.
- 하나의 언더바로 시작하는 이름은, 컴파일러, 리소스가 전역 식별자(global identifier)로 사용하기로 예약되어 있으므로 사용할 수 없다.
Ex. __time_stop 또는 _Donut 과 같은 변수는 사용해도 컴파일 상 오류가 발생하지는 않으나, 정의되지 않은 이상한 동작을 수행하여 논리 오류를 발생시킬 수 있다.
- 변수의 길이에는 제한이 없으며, 변수 이름에 쓰인 모든 문자들은 유효하다. 단, 특정 플랫폼은 고유의 길이 제한이 존재한다.
Ex. ANSI C 에서 변수의 길이는 63자 이내로 제한된다.
변수 이름짓기 Tip
- 숙련된 프로그래머들은 변수의 내용과 데이터형으로 변수의 이름을 짓는다.
Ex. nMyWeight = n : 변수가 정수형임을 의미, MyWeight : 몸무게 값을 저장하는 변수임을 의미
(혹은, intMyWeight 형태로 짓기도 함)
str이나 sz는 null로 끝나는 string을 나타내는 접두어이다.
b는 boolean 값임을 나타내는 접두어이다.
p는 pointer 값임을 나타내는 접두어이다.
c는 단일 문자값임을 나타내는 접두어이다.
메모리의 크기는 Width라 표현한다.
정수형
C++에서는 각각의 데이터형에 고유한 크기의 메모리 공간을 부여하지 않고, 최소 크기만을 정해둠으로써 융통성있는 표준을 제공한다.
통상적으로 1 Byte 는 8 bits의 메모리 단위를 의미하나, C++에서 Byte 는 컴파일러의 기본 문자 세트를 수용할 수 있는 최소한의 연속된 bit들을 의미한다.
즉, ASCII나 EBCDIC을 사용하는 시스템에서 C++의 1 Byte 는 8 bits 이며, Unicode와 같이 더 큰 문자 세트를 사용하는 시스템에서 C++의 1 Byte 는 16 bits 혹은 32 bits 가 되기도 한다.
따라서, 메모리의 사용이 극히 제한되는 시스템에서 변수의 data type 선정은 곧 이윤 창출과도 직접적으로 연관되는 부분이다.(즉, 절약되는 비트는 모두 돈이 된다.)
Ex. 현재 개발하고 있는 시스템에서 short와 int형의 크기가 같더라도 문제가 되지 않는 범위에서는 반드시 short형을 선택해야 소규모 시스템으로 프로그램이 이식되어도 메모리를 차지하는 공간을 최소화할 수 있다.
ex) 16bit int형을 사용하는 DOS 시스템에서 32bit int형을 사용하는 시스템으로 프로그램을 이식할 때, int형 데이터들은 메모리를 기존보다 두 배로 요구하게 되고 이는 곧장 메모리 낭비로 이어짐
* 1 Byte를 8 bits로 명확히 규정시키는 "octet 용어"를 사용하기도 한다.
Data Type |
Memory Width |
char | 4 bits |
short | 최소 16 bits |
int | 최소한 short 보다는 큼 |
long |
최소 32bits, 최소한 int 보다는 큼 |
long long |
최소 64bits, 최소한 long 보다는 큼 |
Example. 현재 시스템에서 data type 별 memory width을 출력하는 프로그램
#include <iostream>
#include <climits>
// 여러 데이터 타입의 한계값을 기호상수(symbolic constant)로 정의해놓은 헤더파일
int main(){
using namespace std;
int n_int = INT_MAX;
short n_short = SHRT_MAX;
long n_long = LONG_MAX;
long long n_llong = LLONG_MAX;
// 각 데이터 타입에 대한 최대값을 나타내는 기호상수
cout << "char = " << CHAR_BIT << " bits" << endl;
cout << "int = " << sizeof(int) << " Byte" << endl;
cout << "short = " << sizeof(n_short) << " Byte" << endl;
cout << "long = " << sizeof(n_long) << " Byte" << endl;
cout << "long long = " << sizeof(n_llong) << " Byte" << endl << endl;
cout << "Max of int = " << n_int << endl;
cout << "Min of int = " << INT_MIN << endl;
cout << "Max of short = " << n_short << endl;
cout << "Max of long = " << n_long << endl;
cout << "Max of long long = " << n_llong << endl;
cin.get(); // 프로그램 자동종료 방지
return 0;
}
* Operator : sizeof()
- 데이터 타입이나 변수의 메모리 크기를 반환하는 연산자
- 피연산자로 데이터 타입을 이용할 경우, 반드시 괄호 안에 기입해야 함
- 피연산자로 변수를 이용할 경우, 괄호는 생략할 수 있음
Ex. sizeof(char), sizeof n_short
sizeof(int) // Operand : Data Type
sizeof n_short // Operand : Variable
변수의 초기화
- 변수에 값을 대입하는 것
- 정의된 변수를 초기화하지 않으면, 그 변수의 값은 미확정(undefined) 상태에 처하는데,
이는 해당 변수가 생성되기 직전에 해당 메모리 위치에 우연히 남아있던 값이 곧 변수의 값으로 될 수도 있음을 의미함
여기서 그 우연한 값을 Garbage value(쓰레기 값)이라고 한다.
- 변수는 선언 즉시, 어떠한 값으로든 초기화 해 놓는 것이, 논리오류를 방지하는 좋은 방법이다.
Example. C에서의 initialize vs C++에서의 initialize
int owls = 101;
// C Style
int wrens(432);
// C++ Style
int rheas = {12}; // rheas를 12로 초기화
int emus{7}; // emus를 7로 초기화
int rocs = {}; // rocs를 0으로 초기화
int psychic{}; // psychics를 0으로 초기화
// Brand new C++ Style
// C++98에서 Array, Structure를 초기화 할 때 사용했던 문법을
// C++11에서는 단일 변수를 초기화할 때에도 사용할 수 있게 되었다.
// 변수 초기화 방법의 통일을 위해 고안된 개념
부호
signed
- 부호를 갖는 변수
- 제한된 메모리 크기 안에서 양수와 음수를 표현해야 하므로 표현 범위가 unsigned type에 비하여 좁음
unsigned
- 부호를 갖지 않는 변수 (항샹 양수)
- 마지막 부호비트(1 bit)까지 수치를 표현하는데에 동원되므로 부호를 갖지 않는 대신, signed type의 표현 범위의 두 배를 표현할 수 있음
- 음수가 될 수 없는 정보를 저장하는 변수(인구 수 정보, 도로의 길이 등)를 unsigned로 선언할 경우, 보다 넓은 범위를 이용할 수 있음
- 코드 상에서, \(\texttt{unsigned}\) 형은 \(\texttt{unsigned int}\) 형이 축약된 형태이다.
오버플로우 & 언더플로우
- 오버플로우 : 변수의 값이 해당 시스템 데이터형의 최댓값을 초과할 때, 최솟값으로 저장되는 현상
- 언더플로우 : 변수의 값이 해당 시스템 데이터형의 최솟값보다 낮아질 때, 최댓값으로 저장되는 현상
진법
number bases
- C++에서는 변수에 상수를 대입할 때, 8진법, 10진법, 16진법 중 어떠한 진법을 이용하여 표현해도 적절하게 인식하여 해당 값을 변수에 이상없이 대입시킨다.
- 프로그래머 입장에서 표기를 쉽게하기 위해 고안된 개념이다.
ex) 16진수 B000을 변수에 대입시키고 싶을 때, 이에 해당하는 10진수 표현인 45056으로 굳이 변환할 필요없이 0xB000을 그대로 소스 코드에 사용할 수 있다.
C++에서 상수의 진법을 인식하는 방식
첫 숫자가 1~9 사이에 해당하는 숫자 -> 10진수 (자연 세계에서 사용)
첫 숫자가 0이고, 두 번째 숫자가 1~7 사이에 해당하는 숫자 -> 8진수 (구 UNIX 시스템에서 사용)
처음 두 개의 문자가 0x 또는 0X -> 16진수 (H/W와 관련이 깊은 진수)
C++에서의 진수 출력 방식
- 상수가 어떤 진법으로 표현되었건 간에, cout은 10진수 형태로 출력하는 것을 기본값으로 한다.
조정자(Manipulator)를 이용한 방식
Manipulator : dec, hex, oct
- std 이름공간 내에 정의되어 있는 조정자들이다.
- cout의 스트림에 조정자가 전달되면, 다른 조정자가 입력되기 전까진 해당 조정자의 진법 출력 방식으로 정수 상수가 출력됨
Example. Manipulator
int A = 147; // 10진수로 147을 의미
int B = 023; // 10진수로 19를 의미
int C = 0x1a; // 10진수로 26을 의미
cout << B << endl;
// cout의 default에 따라, 8진수로 저장되었지만 10진수 형태인 "19"가 출력됨
cout << std::oct << B << std::endl;
// oct 조정자가 전달된 후, B를 출력하므로 "023"이 출력됨
cout << B << std::endl;
// oct 조정자가 전달된 이후 어떠한 조정자도 전달되지 않았으므로 8진수 출력이 유지됨
// 따라서 "023"이 출력됨
cout << std::hex << C << std::endl;
// hex 조정자에 의해 C는 16진수 형태인 "0x1a"가 출력됨
C++이 상수의 데이터형을 결정하는 방법
- 변수의 경우, 프로그래머가 직접 data type을 결정하지만, 상수의 경우는 C++컴파일러가 자체적으로 판단하여 적절한 data type과 메모리 공간을 할당한다.
- C++의 상수 데이터 타입 default는 int형 이다.
- 상수뒤에 특정한 접미어(Suffix)를 이용하여 상수의 data type을 프로그래머가 결정할 수 있다.
* 접미어는 알파벳의 순서가 바뀌어도 상관없고, 대소문자를 구분하지 않는다.
Suffix | Data type |
L, l | long |
LL, ll | long long |
U, u | unsigned int |
UL, ul | unsigned long |
ULL, ull | unsigned long long |
* 알파벳 l과 숫자 1은 혼동하기 쉬우므로, 알파벳 l은 대문자로 표기하는 것이 권장됨
Example. Data Type Suffix for Constant
22022L // "long" data type 상수 22022
149870uLL // "unsigned long long" data type 상수 149870
5473U // "unsigned int" data type 상수 5473
접미어가 생략된 경우의 상수 저장 방법
- 접미어가 생략된 10진 정수의 경우, int, long, long long 타입 중 크기가 가장 작은 데이터 타입으로 저장됨
ex) 16bit int 타입과 32bit long 타입을 사용하는 시스템에서,
20,000은 int형으로, 40,000은 long형으로, 3,000,000,000은 long long형으로 저장됨
- 접미어가 생략된 8진, 16진 정수는 int, unsigned int, long, unsigned long, long long, unsigned long long 형 중에서 크기가 가장 작은것으로 나타냄
문자형
Data Type : char
- chracter 의 약어
- 문자와 숫자를 저장하는 데이터 타입
- 대개, 프로그래밍 언어들은 문자를 수치 코드로 저장함 (ex. in ASCII 'M' = 77)
- 1 Byte의 메모리가 할당되며, 대부분의 컴퓨터 시스템들은 256개 이하의 문자들을 지원하므로 char형엔 1 byte만 할당하게 되었음
* char형 또한 signed 타입과 unsigned 타입으로 구분되는데, 이는 char형으로 수치를 나타내고자 할 때 중요함
* int형과 달리 char형은 signed형과 unsigned형 둘 중 하나로 미리 정해져있지 않음
signed char : -128 ~ +127
unsigned char : 0 ~ +255
ex. 수치값 200을 일반적인 char형 변수에 저장할 경우, 어떤 시스템에선 정상적으로 동작하지 않을 수 있음(default가 signed char형인 시스템에서)
- 그러나 표준 ASCII 문자를 char형 변수에 저장할 때는, signed형이나 unsigned형이나 관계없이 정상적으로 동작함
Data Type : wchar_t
- 프로그램이 1 Byte 내에서 커버할 수 없는 문자 세트(일어, 중국어, 한국어 문자세트 등)를 처리하는 방법엔 두 가지가 있는데,
첫 번째는 확장 문자 세트가 시스템의 기본 문자 세트라면, 컴파일러 개발업체가 처음부터 char형에 2 Byte 이상의 메모리를 할당하는것이고,
두 번째는 기본 문자 세트와 확장 문자 세트를 동시에 지원하는 방식으로 해결한다.
= 보통의 8비트 char형으로 기본문자세트를 표현하고,
= wchar_t형으로 확장문자세트를 표현하는 방법
* wchar_t = 시스템에서 사용되는 가장 큰 확장문자세트를 커버할 수 있을만큼 충분한 메모리(2 Byte 수준)를 할당받는 데이터 타입
-cin과 cout은 입출력을 char형 문자의 스트림으로 간주하므로 wchar_t의 입출력을 지원하지는 못하고,
- iostream 헤더 파일에서는 wchar_t형을 위해 wcin과 wcout객체를 지원한다.
- 확장 문자 상수나 확장 문자열은 그 앞에 'L'을 붙여서 구분한다.
ex.
wchar_t bob = L'P';
wcout << L"tall" << endl;
* 특별한 경우, 문자열을 변환할 때 길이, 부호가 고정될 필요가 있는데 wchar_t 의 부호와 길이는 가변이기 때문에 문제가 발생할 때가 있었고, 이를 보완하기 위해 C++11에서는 char16_t와 char32_t 데이터 타입을 지원한다.
Data Type : char16_t
- unsigned 16 bit 데이터 타입
- 소스 코드내에서 문자 상수, 문자열을 이용할 때 접두사로 'u'를 사용함
ex. u'C', u"be good" 등
ex. \u00F6 (네 자리 16진수 코드 = 16 bit) 형태의 유니버셜 문자 이름과 매칭됨
Data Type : char32_t
- unsigned 32 bit 데이터 타입
- 소스 코드내에서 문자 상수, 문자열을 이용할 때 접두사로 'U'를 사용함
ex. U'C', U"be good" 등
ex. \U0000222B (여덟 자리 16진수 코드 = 32bit) 형태의 유니버셜 문자 이름과 매칭됨
문자 상수
- 연산시에, 문자는 변수이름과의 구분을 위해 문자 상수로 표현하여 사용한다.
- 문자 상수는 작은 따옴표(Single Quotes, '')로 문자를 감싸는 방식으로 표현한다.
ex. 'M'
- 문자열은 큰 따옴표 (Double Quotes, "")로 문자열을 감싸는 방식으로 표현한다.
ex. "Hi World!"
문자의 입력과 출력
- char형 변수에 문자에 해당하는 수치 코드를 입력하는 것이 아닌, 문자 그 자체를 입력해도 알맞는 수치 정보가 자동으로 환산되어 변수에 저장됨 (물론, 수치 코드를 입력해도 정상적으로 처리됨)
- 출력또한 수치 코드가 아닌 문자로 알맞게 변환되어 출력됨
- 이같은 자동적인 입출력 기능은 cin객체와 cout객체에 내장된 기능임
- 문자들은 기본적으로 수치 코드로 나타내어 지는데, 이 수치 코드를 문자로 해석할 것인지 수치로 해석할 것인지는 I/O 시스템 (입출력시스템)이 결정한다.
관련 함수
1. cout.put(argument)
- cout객체의 member function
- . = membership operator
- C와 C++ 초기 버전들은 문자 상수를 int형 으로 저장하였는데, 그시절(C++ Release 2.0 이전) cout객체는 문자는 문자 그대로를 출력하였지만, 문자 상수는 수치로 출력을 하여 문제가 됬었음
- 이러한 문제의식으로 인해 cout.put()함수가 탄생함
- 사실 C++ Release 2.0 이후부터 문자 상수를 int형이 아닌 char형으로 저장하면서, cout의 문자 출력기능이 개선되고 cout.put() 함수의 필요성이 희미해짐
문자코드체계
ASCII : 미국에서 가장 많이 사용하는 문자 세트
EBCDIC(eb-se-dik, 엡세딕) : IBM Mainframe에서 사용하는 문자 세트
Unicode : 국제 문자 세트(확장 문자형, wchar_t형 이용)
* 문자세트별로 문자의 수치 코드가 상이하므로, 소스 코드에서 문자의 수치 코드보다는 문자 상수 그 자체를 이용하는 것이 특정 코드 체계에 종속적이지 않게 되므로 권장됨
Escape Sequence
- 키보드를 통해 일반적인 입력이 어렵거나 C++내에서 특별한 의미를 지니고 있는 특수문자들을 위한 표기법
Character name | ASCII Symbol | C++ code |
개행 | NL (LF) | \n |
수평 탭 | HT | \t |
수직 탭 | VT | \v |
백스페이스 | BS | \b |
캐리지 리턴 | CR | \r |
경보 | BEL | \a |
백슬래시 | \ | \\ |
물음표 | ? | \? |
작은따옴표 | ' | \' |
큰따옴표 | " | \" |
- 여타 문자상수와 마찬가지로 특수문자를 문자상수로써 이용하고자 할 때는 작은따옴표('')로 감싸서 표현하도록 한다.
- 문자열 내에서 사용될 때는 작은 따옴표로 감싸지 않는다.
- Escape sequence 또한 기호 이스케이프 시퀀스 (symbol E.S.에 대한 수치 코드(수치 이스케이프 시퀀스, numeric E.S.)가 존재하는데, 예를 들어 \b의 수치 코드는 \x8 (16진수) 또는 \010 (8진수) 이다.
- 수치 이스케이프 시퀀스는 특정 코드 체계에 종속적이지만, 기호 이스케이프 시퀀스는 모든 코드 체계에서 호환되므로, 가급적 기호 이스케이프 시퀀스의 이용을 권장함, 심지어 사용하기에 더 쉽기도 하니까)
유니버셜 네임 코드
- 사용자의 입력장치와는 독립적으로, 소스 코드상에서 국제 문자들을 표현하는 메커니즘
- 일반적인 키보드로 표현할 수 없는 문자들(독어의 변모음, 불어의 엑센트 등)을 변수의 이름으로 사용 가능하게 함
- 사용중인 C++ 시스템이 ISO 10646을 지원해야 함
* 표현방법
- 역슬래시 + 'U' 또는 'u' + ISO 10646코드명
ex. \U00F6 = \(\ddot{o}\)
* 유니코드는 각각의 character 에 교유의 code point를 부여하는데,
전형적인 code point의 형태는 U-222B와 같은 형태이며 U는 Unicode를, 222B(16진수)는 해당 문자의 고유번호를 의미한다.
Boolean Type
- ANSI/ISO C++표준에 추가된 새로운 데이터 타입
- True(0이외의 값, 대표적으로 1), False(0) 두 가지 값 중 한 값만 가질 수 있는 데이터 타입
bool A = true; // A에 true가 대입됨
bool D = -100; // D에 true가 대입됨
bool E = 0; // E에 false가 대입됨
int B = true; // B에 1이 대입됨
int C = false; // C에 0이 대입됨
Auto Type (추후에 자세한 내용 추가기재)
- 초기화 시, 데이터형 명시를 생략하는 방법
- 컴파일러가 변수에 대입되는 수치에 알맞는 데이터형을 자동으로 선정한다.
- STL을 사용할 때와 같이 복잡한 변수형을 다룰 때 유용하다.
// auto type을 이용하지 않는 기존의 방법
std::vector<double> scores;
std::vector<double>::iterator pv = scores.brgin();
// auto type을 이용한 소스코드 간략화
std::vector<double> scores;
auto pv = scores.begin();
* Auto type 이용시 주의사항
세 변수 A, B, C 모두 double형으로 선언되길 원했다고 가정했을 때, 아래 예시 코드와 같은 문제가 발생할 수 있다.
Example. Abusing of Auto type
auto A = 0.0;
// 0.0에 걸맞는 데이터형인 double형이 A의 데이터형으로 배정될 것이다.
double B = 0;
// 이미 변수 B에 대한 데이터형으로 double을 명시해놓은 상태이다.
auto C = 0;
// 0에 걸맞는 데이터형인 int형(적어도 double형은 아닐것임)이 C의 데이터 형으로 배정될 것이다.