TIL 2025.04.24 기록
0. 개요
1. 뇌를 자극하는 C++ 정리
타입: 변수가 보관할 수 있는 값의 종류
C++에서 제공하는 타입이란 기본적인 정수, 실수, 문자 타입에 이것 저것 옵션을 달아서 확장한 것 밖에 되지 않는다.
정수를 계산하는 것보다 실수를 계산하는 것을 훨씬 더 힘들어 한다.
이와 같이 짧은, 긴 정수로 구분한 것은 변수가 차지하는 공간을 절약하기 위한 것이다.
C++에서 다양한 타입을 제공하는 이유는 개발자들에게 보다 많은 선택권을 주기 위해서다.
다양한 종류의 int 타입
short int |
unsigned short int |
int |
unsigned int |
long int |
unsigned long int |
※변수가 받아들일 수 없는 값을 대입한 경우에는 전혀 엉뚱한 값이 대입되는 결과를 받는다.
어느 타입을 사용할 지에 대한 가이드라인
- 보관하려는 값이 short int에 들어갈 수 있다면, short int를 사용하라.
- 만약 더 크고, 음수가 아니라면, unsinged short int를 사용하라.
- 그것도 모자란다면 int, long int를 사용하라.
보관할 수 있는 값의 범위가 클 수록 자원을 많이 소비하기 때문에 가능한 작은 범위의 타입으로 알맞게 사용하는 것이 중요하다.
※ 파일에 사용하거나, 네트워크에 보내는 등이 아닌, 잠깐 변수를 사용하는 것이라면 시스템 자원을 아끼려 노력하지 않아도 된다.
보다 실질적인 가이드라인
- 정수를 담을 때는 int를 사용하자.
- 보관하려는 값이 절대 음수가 될일이 없다면 unsigned int를 사용하자.
※ 더 큰 정수 보관법.
C++에만 존재하는 대용량 정수 타입 _int64 등이 있다.
보다 확실한 방법은 long int와 같은 기본 타입의 변수를 여러개 묶어서 사용하는 것이다.
차후에 배울 클래스를 사용하면 이런 기능을 하는 새로운 타입을 만들 수 있다.
비트(bit)와 바이트(byte)
비트와 바이트는 컴퓨터에서 정보를 저장하는 최소 단위이다. 비트는 0또는 1을 나타내는 전기 장치로 생각하면 편하다. 불이꺼지면 0, 켜지면 1 따라서 1bit = 2가지 상태 |
short int = 2byte 2byte = 16 bit = 2^16가지의 상태 = 65536 int나 long int는 4byte 4byte = 32 bit = 2^32가지의 상태 = 4294967296 |
바이트는 이러한 비트가 8개 모인 것을 말한다.
컴퓨터의 메모리는 바이트 단위로 공간을 나누어두었다.
변수에 값이 보관된다고 할때 그 값은 컴퓨터의 메모리에 보관하는 것 이다. 결국 변수가 얼마나 큰 값을 보관하고 있는 지는 변수가 몇개의 바이트로 이루어져 있는지에 달렸다.
역으로 이야기 하면, 많은 상태의 표현이 가능한 타입은 더 많은 시스템 자원을 차지한다.
sizeof() : 괄호 안에 타입 이름을 넣어주면 해당 타입의 크기가 나오게 된다.
이때 타입 이름에 변수 이름을 넣어줘도 되지만, 결과값은 해당 변수의 타입크기가 반환된다.
C++ 표준안은 정수타입의 크기를 정확하게 명시하고 있지 않다.
최소한의 규정
- short int의 크기는 적어도 2바이트보다 커야한다.
- int의 크기는 적어도 short int의 크기보다 커야한다.
- long int의 크기는 적어도 4바이트보다 커야하고 int의 크기보다 커야한다.
10 진수, 8 진수, 16 진수 표현 (Decimal, Octal, Hexadecimal)
가끔씩은 10진수보다 8진수, 16진수를 사용하는 것이 편할 때가 있다.
8 진수와 16 진수는 사용하기 전 컴퓨터에 알릴 필요가 있는데, 8진수나 16진수로 숫자를 적을 때는 숫자 앞에 공백 없이 0또는 0x를 붙여주면 된다. (숫자 0이다.)
※ 입력은 8진수나 16진수로 해도 결과창에 출력하는 숫자는 기본적으로 10진수라 10진수로 출력한다.
실수 타입
실수를 보관할 수 있는 타입 (float, double, long double)을 보통 부동 소수점 타입이라 한다.
이때 부동은 “움직이지 않는다”라는 의미가 아니라 “둥둥~ 떠다닌다”는 의미이다.
즉, 소수점을 이리저리 옮겨다닐 수 있다는 뜻이다.
float f = 0.00123f; // C++에서는 실수가 뒤에 f가 붙어있지 않으면 double 타입에 상응하는 실수 값이라고 생각한다.
double d = 1.23;
long double ld = 123.0;
f = 1.23E-3f;
d = 1.23E0;
ld= 1.23E2;
문자 타입
char, wchar_t는 문자를 보관하는데 사용한다.
wchar_t는 세계 각국의 문자와 기호들을 표현할 수 있다.
실제 프로그래밍 시에는 char를 주로 사용하고 wchar_t는 특별히 요구되는 상황에서 사용하게 된다.
문자 타입의 변수에는 오직 하나의 문자만 보관할 수 있다.
또한 문자 상수를 표현할 때는 작은 따옴표 (’ ‘)로 감싸준다.
큰 따옴표(” “)로 감싸는 경우는 여러 개의 문자를 사용하는 경우에 해당된다. 이는 문자열이라고 부르는 것으로 뒤에서 배우게 된다.
char 형은 보통 1바이트의 크기를 갖는다. 그래서 char형이 가질 수 있는 모든 상태는 2^8 즉, 256가지가 된다.
ASCII ( American Standard Code for Information Interchange)
ASCII는 아스키 코드라고 부른다.
#include <iostream>
using namespace std;
int main()
{
// 'A'의 아스키 코드 값 확인
int a = 'A';
cout << a << "\n";
// 65를 문자로 표현하면?
char b = 65;
cout << b << "\n";
return 0;
}
정수 타입이라면 65가 정수로 해석될 것이고 문자 타입이라면 아스키 코드표에 따라 65가 'A'의 의미로 해석될 것이다.
이스케이프 문자열 (Escape Sequences)
이스케이프 문자열의 아이디어는 매우 간단하다. “화면에 엔터를 출력하고 싶을 때는 어떻게 할까?” 혹은 “화면에 백스페이스를 출력하고 싶은 때는 어떻게 할까?”에 대한 질문의 답이다.
모든 이스케이프 문자열은 역 슬래쉬(\)로 시작한다.
특수 문자 | 아스키 코드 상에서의 표현 | C++에서의 표현 |
개행 문자 | NL(LF) | \n |
수평탭 | HT | \t |
수직탭 | VT | \v |
백스페이스 | BS | \b |
캐리지 리턴 | CR | \r |
폼 필드 | FF | \f |
벨소리 | BEL | \a |
역슬래쉬 | \ | \\ |
물음표 | ? | \? |
작은 따옴표 | ' | \' |
큰 따옴표 | " | \" |
8진수 | 000 | \000 |
16진수 | hhh | \xhhh |
넓은 문자열의 사용
wchar_t 타입은 보통 2바이트의 크기를 갖기 때문에 char타입보다 훨씬 많은 문자를 표현할 수 있다.
wchar_t는 아스키 코드 개신에 유니코드(Unicode)와 같이 세계 각국의 문자와 기호를 정의해놓은 코드를 사용한다.
wchar_t wc;
wc = L' ( ';
문자 상수 앞에 L을 붙이면 이 상수가 wchar_t 타입에 상응한다는 의미가 된다.
bool 타입
bool 타입의 변수에는 true 혹은 false의 값이 들어갈 수 있다.
실제 코딩을 해보면 변수의 값이 true나 false가 아닌 1과 0으로 나오는 것을 확인할 수 있다.
타입의 표시와 실제적인 의미
컴퓨터가 받아들이는 타입 | 표시한 타입 |
"char" | char |
"unsigned char" | unsigned char |
"signed char" | signed char |
"bool" | bool |
"unsigned int" | unsigned, unsigned int |
"int" | signed, signed int, int |
"unsigned short int" | unsigned short int, unsigned short |
"unsigned long int" | unsigned long int, unsigned long |
"long int" | signed long int, signed long, long int, long |
"short int" | signed short int, signed short, short int, short |
"wchar_t" | wchar_t |
"float" | float |
"double" | double |
"long double" | long double |
"void" | void |
형변환 (casting)
왜 형변환을 배워야 할까?
형변환(casting)이라는 주제는 C++프로그래밍에 있어서 아주 중요한 부분을 차지하고 있다. 형변환은 여러가지 경우에 발생할 수 있는데, 그 중에서 대표적인 경우가 한 변수에 보관되어 있던 값을 다른 타입의 변수에 대입하는 경우다.
형변환은 아주 흔하게 발생한다. 여러분이 의식하지 못하는 사이에도 컴퓨터에 의해서 형변환이 발생한다. 또한 여러분이 직접 형변환을 시켜주어야 하는 경우도 자주 발생한다. 그만큼 형변환은 C++ 개발자의 일상 생활에서 빼놓을 수 없는 주제다. 우선 여러분은 한 타입에서 다른 타입으로 형변환이 발생하는 경우에, 그 안에서는 어떤 일이 발생하는지 알고 있어야 한다. 값이 어느 정도 잘리는 경우도 있고, 정밀도가 떨어지는 경우도 있으며, 전혀 엉뚱한 값으로 바뀌는 경우도 있다. 물론 보통의 경우는 아무런 문제 없이 형변환을 수행한다. 다음으로 여러분은 직접적으로 형변환을 명령함으로써 여러분의 의도를 밝히는 법을 배워야 한다. 지금 이 단락에서 형변환의 모든 것을 배우지는 않을 것이다. C++에 대해 더 많은 내용을 배울수록 더 다양한 형변환을 접하게 된다. 여러분은 아직 많은 것을 배우지 못했으므로 여기에서는 형변환의 기본적인 사용만을 배우게 될 것이다.
형 변환의 규칙
- 실수 타입에서 정수 타입으로 형변환이 발생하는 경우에는 실수의 소수점 이하 부분이 잘리게된다. 반올림이 아님에 주의하자.
- 모든 타입에서 bool 타입으로 형변환할 때, 모든 0이 아닌 수는 true 즉, 1로 바뀐다. 0은 그대로 0으로 남아 false의 의미를 갖는다.
문제가 발생하는 형변환
문제가 발생하는 형변환의 공통적인 조건은 큰 타입에서 작은 타입으로 형변환이 일어난다는 것이다.
큰 타입의 변수에 담겨있던 값이 작은 타입의 변수에 담길 수 없을 만큼 크다면 물리적인 한계로 인해서 올바른 값을 담을 수 없게 된다.
이런 경우에는 값의 일부가 잘려나가거나 예상할 수 없는 이상한 값으로 변질되어 버린다.
#include <iostream>
using namespace std;
int main()
{
// 정수 타입간의 형변환
int i = 131071;
unsigned short i2us = i; // int -> unsigned short
short i2s = i; // int -> short
// 실수 타입간의 형변환
double d = 12345678901234.56789;
float d2f = d; //double -> float
// 실수 타입에서 정수 타입으로 형변환
float f = 76543.21f;
short f2s = f; // float -> short
// 정수 타입에서 실수 타입으로 형변환
int big_i = 1234567890;
float i2f = big_i; // int -> float
// 결과 출력
cout << fixed;
cout << "int : " << i << "\n\tto unsigned short : " << i2us << "\n\tto short : " << i2s << "\n\n";
cout << "double : " << d << "\n\tto float : " << d2f << "\n\n";
cout << "float : " << f << "\n\tto short : " << f2s << "\n\n";
cout << "int : " << big_i << "\n\tto float : " << i2f << "\n\n";
return 0;
}
예제 코드에 대한 설명은 키포인트 코너에 자세하게 나와있다. 이상한 값으로 변질된다는 말은 사실 어떻게 변할지 예상할 수 없다는 뜻이다. 보다 구체적으로 말하면 표준문서에서 “이렇게 변해야 한다.”라는 규정이 없다는 의미다. 중요한 것은 “어떻게 변하느냐”가 아니라 “언제 문제가 발생하느냐”다. 언제 이런 문제가 발생하는지 알고 있고, 그 상황을 피하는 것이 중요하다.
명시적인 형변환 (explicit conversion)
프로그래밍 서적을 읽다보면, 특히나 번역서를 읽다보면 명시적이라는 말과 암시적, 혹은 묵시적 이라는 단어가 자주 등장한다.
명시적 (explicit) : 눈에 보이는 방식으로 직접 무언가 하는 것을 말한다.
암시적 (inplicit) : 눈에 보이지 않게 자동적으로 수행되는 것을 말한다.
지금까지 살펴본 형변환(conversion)은 모두 암시적인 형변환이었고 이번에는 직접 형변환을 명령하는 방법에 대해서 배워볼 것이다.
#include <iostream>
using namespace std;
int main()
{
// 다양한 타입의 변수 정의
int i = 65;
float f = 66.89f;
char c = 'C';
// 명시적으로 형변환 한다.
cout << "int i = " << i << "\n";
cout << "(char)i = " << (char)i << "\n";
cout << "(bool)i = " << (bool)i << "\n\n";
cout << "float f = " << f << "\n";
cout << "(int)f = " << (int)f << "\n";
cout << "char c = " << c << "\n";
cout << "(int)c = " << (int)c << "\n";
return 0;
}
명시적인 형변환 사용법 : 명시적인 형변환을 위해서는 변수 혹은 값의 앞쪽에 괄호와 함께 타입을 적어주면 된다.
한 가지 알아두어야 할 것은 명시적인 형변환에는 임시 변수가 수반된다는 사실이다.
이 변수는 우리에게 보이지 않지만 컴퓨터가 내부적으로 사용하려는 목적으로 만드는 변수다.
즉, 우리가 (char)i라고 적었을 때, 내부적으로 char 타입의 변수가 하나 만들어지고, i의 값이 대입되면서 형변환을 하는 것이다.
나중에 임시 객체 때문에 발생하는 문제점이나, 성능 저하에 대해서 다룰것이다.
명시적인 형변환을 사용하는 목적 : 의도를 컴퓨터에게 알려주는 것이다.
- float 타입에서 int 타입으로 형변환을 할 경우 소수점 이하 부분이 잘리게 되는 문제가 발생한다. (이때 컴퓨터는 소수점 이하부분이 잘리게되어 값이 변한다는 경고를 준다.) 사용자가 소수점 이하부분을 잘라내기 위해서 일부러 형변환시킨 것이라면 명시적인 형변환을 사용해서 그 의도를 명확히 할 수 있다.
- 뿐만 아니라 코드를 처음 보게될 사람에게까지 의도를 알리는 역할을 할 수 있다.