• 2.6.1 Char type
  • 2.6.2 limits 헤더를 이용한 char 한계치 확인
  • 2.6.3 입력이 문자열이고 받는 메모리가 문자인 경우 버퍼의 작동 방식
  • 2.6.4 강제 형 변환

2.6.1 Char type

 

Char type은 문자를 다루는 자료형이다. 문자의 경우 ASCII CODE로 대응시켜서 다루게 되며, 이 때문에 char 자료형에 숫자를 저장해서 출력하는 경우 숫자가 아닌 문자가 나오게 된다. 출력 시 문자가 나오지만 숫자로 다룰 수 있는 특징이 있다. 문자를 대입하는 경우 홑따옴표(' ')를 사용하게 된다.

#include <iostream>

int main()
{
	using namespace std;

	char c1(65);
	char c2 = 'A';
    
	cout << c1 << " " << c2 << " " << int(c1) << " " << int(c2) << endl;

	return 0;
}

위의 코드를 실행하게 되면 둘 다 문자로는 A가 출력되고 숫자로는 65가 출력되는 것을 확인할 수 있다.


2.6.2 limits 헤더를 이용한 char 한계치 확인

 

아래의 코드를 통해 char 자료형의 한계치를 확인할 수 있다.

#include <iostream>
#include <limits>

int main()
{
	using namespace std;

	cout << sizeof(char) << endl;
	cout << (int)numeric_limits<char>::max() << endl;
	cout << (int)numeric_limits<char>::lowest() << endl;

	return 0;
}

char 자료형은 1byte의 크기를 가지며 양수는 127, 음수로는 -128까지 표현이 가능하다. 이러한 특징 덕분에 네트워크 통신 분야에서는 이를 작은 크기의 정수로 다루어서 사용하기도 한다.


2.6.3 입력이 문자열이고 받는 메모리가 문자인 경우 버퍼의 작동 방식

 

앞에서도 언급하였듯이 문자로 할당된 메모리는 하나의 값만 받는다. 하지만 사용자가 입력을 문자가 아닌 문자열로 입력하는 실수를 저지르면 어떻게 작동이 될까?

#include <iostream>

int main()
{
	using namespace std;

	char c1(65);

	cin >> c1;
	cout << c1 << " " << static_cast<int>(c1) << endl;

	return 0;
}

c1의 초기화는 65로 A가 저장되어 있다. 여기에 cin을 통한 사용자 입력을 받기로 한다. 하지만 사용자가 실수로 qwe를 입력하였다. 결과는 아래와 같다.

 

q만 받는다.

 

qwe가 입력되었지만 q만 c1에 저장해서 출력하는 것으로 프로그램이 끝나게 된다. 그럼 we는 자동으로 버려지도록 동작할까? 이번에는 위의 코드에서 입력을 두 번 더 받을 수 있도록 작성해서 코드를 작성하고 똑같이 qwe를 입력한다.

#include <iostream>

int main()
{
	using namespace std;

	char c1(65);

	cin >> c1;
	cout << c1 << " " << static_cast<int>(c1) << endl;

	cin >> c1;
	cout << c1 << " " << static_cast<int>(c1) << endl;

	cin >> c1;
	cout << c1 << " " << static_cast<int>(c1) << endl;

	return 0;
}

실행 결과는 아래와 같다.

 

qwe를 하나씩 출력한다.

 

이처럼 stream을 사용하게 되었을 때 문자열을 길게 나열해서 넣으면 받는 메모리 길이에 따라 자동으로 받고 남는 부분을 버퍼에 남기도록 동작하게 된다.


2.6.4 강제 형 변환

 

형 변환으로 여러 가지가 존재한다. 프로그래밍하면서 자주 본건 c-style casting이었다. cpp-style casting을 직접 써보진 않았지만 지금 보면서 생각해보면 아래처럼 제대로 감싸서 형 변환시키는 것이 안전할 거란 생각이 든다. static_cast도 존재하는데 이는 나중에 추가적으로 설명할 예정이다.

#include <iostream>
#include <limits>

int main()
{
	using namespace std;

	// c-style casting(강제 변환)
	cout << (char)65 << endl;
	cout << (int)'A' << endl;

	//cpp-style casting(강제 변환)
	cout << char(65) << endl;
	cout << int('A') << endl;

	// 추후 설명(기본형에 대한 변환을 컴파일러에게 미리 알림)
	cout << static_cast<char>(65) << endl;
	cout << static_cast<int>('A') << endl;

	// 위의 3개는 같은 결과를 보인다.


	//static_cast를 사용한다고해서 기존에 있는 것이 변하는 것은 아님
	char ch(97);
	cout << ch << endl;
	cout << static_cast<int>(ch) << endl;
	cout << ch << endl;

	return 0;
}
  • 2.5.1 Boolean type
  • 2.5.2 사용 시 주의사항

2.5.1 Boolean type

 

Boolean 자료형은 참인지 거짓인지에 대한 값을 가지는 자료형이다. true는 1의 값을 가지며, false는 0의 값을 가진다. 따라서 일반적인 출력을 실행할 경우 true와 false는 각각 1과 0을 출력하게 된다. 콘솔 창에서 true/false로 출력하고 싶다면 std::boolalpha를 통해 변경할 수 있다.

 

#include <iostream>

int main()
{
	using namespace std;

	bool b1 = true;	// copy initialization
	bool b2(false);	// direct initialization
	bool b3{ true };	// uniform initialization
	b3 = false;	// assignment operator

	cout << std::boolalpha;
	cout << b3 << endl;
	cout << b1 << endl;
	cout << std::noboolalpha;
	cout << b3 << endl;
	cout << b1 << endl;

	return 0;
}

2.5.2 사용시 주의사항

 

Boolean 자료형을 사용할 경우 여러 가지 주의사항이 있다. 첫 번째로 not operator인 !를 사용하는 경우인데, 해당 연산자는 눈에 잘보이지 않기 때문에 협업하는 사람들이 힘들어질 수 있으므로 최대한 사용 안 하도록 프로그래밍을 하는 것이 좋다. 또한 c++에서는 0을 제외한 모든 수를 true로 인식하고서 조건을 받아들인다.

 

#include <iostream>

int main()
{
	using namespace std;

	// ! not operator 안쓸 수 있다면 안쓰는게 좋다.

	// 논리 연산자 &&(and) 
	cout << (true	&& true)	<< endl; // true
	cout << (true	&& false)	<< endl; // false
	cout << (false	&& true)	<< endl; // false
	cout << (false	&& false)	<< endl; // false

	// 논리 연산자 ||(or) 
	cout << (true	|| true)	<< endl; // true
	cout << (true	|| false)	<< endl; // true
	cout << (false	|| true)	<< endl; // true
	cout << (false	|| false)	<< endl; // false

	// 0을 제외한 모든 수는 true
	if (5)
		cout << "This is true" << endl;
	else
		cout << "This is false" << endl;

	bool b;
	cin >> b;
	cout << "Your input : " << b << endl;

	return 0;
}

 

입력값에 true 혹은 false라고 입력하면 모두 true로 출력할 수도 있다. 이는 방금 언급한 내용에 의한 것이며, 입력값을 문자열로 다루기 때문에 false를 0으로 인식하지 못하기 때문이다. 하지만 컴파일러마다 다르고 버전마다 다르니 이처럼 모호한 입력을 주지 않도록 해야 한다. 0을 입력하면 정확히 false로 인식한다.

'Programming Language > C++' 카테고리의 다른 글

Section 2.7. 리터럴 상수와 심볼릭 상수  (0) 2021.10.19
Section 2.6. char 자료형  (0) 2021.10.18
Section 2.4. 부동소수점수(floating point numbers)  (0) 2021.10.14
Section 2.3. void  (0) 2021.10.12
Section 2.2. 정수형  (0) 2021.10.11
  • 2.4.1 부동소수점수(floating point numbers) 종류
  • 2.4.2 limits 헤더를 이용한 부동소수점수 한계치 확인
  • 2.4.3 과학적 표기법(scientific notation)
  • 2.4.4 Floating error
  • 2.4.5 inf and nan

2.4.1 부동소수점수(floating point numbers) 종류

 

부동소수점수는 float, double, long double로 총 3가지가 c++에 존재한다. 부동소수점이라고 불리는 이유는 과학적 표기법(scientific notation)에 의해 주어진 수의 소수점 위치가 이동하기 때문이며, 부동은 움직이지 않는다는 의미가 아닌 떠다닌다의 의미로 받아들이면 된다.

 

float 자료형은 딥러닝에서 자주 쓰고 기본 실수 계산은 double을 선호하게 된다. 딥러닝이 아닌 일반 수치해석적 과학 계산에서는 double을 사용하는 것을 선호한다.

 

float-point numbers(32 bit)

 

float-point numbers(64 bit)

 

부동소수점수는 부호와 지수, 가수를 통해 구현이 된다. 이러한 표현 방식 때문에 실제 값과 입력 값 차이의 오차가 생기는 한계가 생긴다.


2.4.2 limits 헤더를 이용한 부동소수점수 한계치 확인

 

sizeof 연산자를 이용하면 각 자료형에 대한 크기를 알 수 있다. 이전과 동일하게 limits 헤더를 이용하면 해당 자료형의 한계치를 확인할 수 있다. lowest 함수를 사용해야 가장 작은 음수를 확인할 수 있다.

 

#include <iostream>
#include <limits>

int main()
{
	using namespace std;

	cout << sizeof(float)			<< endl; // 4 bytes
	cout << sizeof(double)		<< endl; // 8 bytes
	cout << sizeof(long double)	<< endl; // 8 bytes

	cout << numeric_limits<float>::max()		<< endl; // min() lowest()
	cout << numeric_limits<double>::max()		<< endl;
	cout << numeric_limits<long double>::max()	<< endl;

	return 0;
}

2.4.3 과학적 표기법(scientific notation)

 

지수 표현을 활용하여 과학적 표기법을 쓸 수 있다. 지수 표현의 경우 과학 계산에서 상수로 쓸만한 값들에 대해 쓰는 것 이외에는 볼 수가 없지만 실무에서의 규칙은 모든 회사가 다르기 때문에 그에 맞게 사용하면 된다.

 

#include <iostream>

int main()
{
	using namespace std;

	// scientific notation
	cout << 3.14		<< endl;
	cout << 31.4e-1	<< endl;
	cout << 31.4e-2	<< endl;
	cout << 31.4e1		<< endl;
	cout << 31.4e2		<< endl;

	return 0;
}

 

e-1은 1/10을 의미하며, e-2은 1/100을 의미한다. +에 대해서는 역수를 취하면 된다. 또한 +는 생략이 가능하다.


2.4.4 Floating error

 

float 자료형이 아닌 double 자료형도 흔히 사용하는 이유는 바로 floating error 때문이다. 메모리를 많이 사용하면 더 세밀한 실수를 다룰 수 있는데, float 자료형을 쓸 경우 소수점 아래 6번째 자리부터 신뢰하기 힘들어진다. double 자료형도 소수점 아래 16번째 자리부터 신뢰하기 힘들어지는데 일반적으로 10자리를 넘어가는 자리수에 대해서는 고려하지 않는다. 또한 콘솔 창에서 표기된 수는 setprecision으로 정확히 보는 게 아닌 이상 믿을 수 없다.

 

#include <iostream>
#include <iomanip> // 입출력 조작

int main()
{
	using namespace std;

	cout << std::setprecision(25);
	cout << 1.0f / 3.0f	<< endl;
	cout << 1.0 / 3.0	<< endl;

	float f1 = 123456789.0f; // 10 significant digits
	cout << f1 << endl;

	double d1 = 1.0;
	cout << d1 << endl;
	cout << std::setprecision(30);
	cout << d1 << endl;

	double d2 = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;
	cout << d2 << endl;

	return 0;
}

 

위의 코드를 실행하면 여러가지 의미 있는 결과들을 볼 수 있다. 특히 d1과 d2의 결과는 기억해둘 필요가 있다.


2.4.5 inf and nan

 

0으로 나누는 것을 수학에서 정의하지 않지만 코드를 작성하면서 간혹 0으로 나누는 일을 수행할 수 있다. 0으로 나누는 경우 그 수는 inf로 표기되는데, 무한대라고 보면 된다. 또한 수가 아닌 경우엔 not a number의 약자인 nan으로 표기된다. cmath 헤더에는 인자로 들어오는 수가 inf인지 nan인지 체크해주는 함수가 있다.

 

#include <iostream>
#include <cmath>

int main()
{
	using namespace std;

	double zero	= 0.0;
	double posinf	= 5.0 / zero;
	double neginf	= -5.0 / zero;
	double nan	= zero / zero;

	cout << posinf	<< endl;
	cout << neginf	<< endl;
	cout << nan	<< endl; // -nan(ind) indeterminate
	cout << posinf	<< " " << std::isinf(posinf) << endl;
	cout << neginf	<< " " << std::isinf(neginf) << endl;
	cout << nan	<< " " << std::isnan(nan) << endl;
	cout << 1.0	<< " " << std::isnan(1.0) << endl;

	return 0;
}

'Programming Language > C++' 카테고리의 다른 글

Section 2.6. char 자료형  (0) 2021.10.18
Section 2.5. Boolean 자료형  (0) 2021.10.15
Section 2.3. void  (0) 2021.10.12
Section 2.2. 정수형  (0) 2021.10.11
Section 2.1. 기본 자료형  (0) 2021.09.29
  • 2.3.1 무치형(Void)

2.3.1 무치형(Void)

 

흔히 void라고 말하는 무치형 타입은 함수에서 반환값이 없거나 매개변수가 없을 경우 사용하게 된다. 하지만 매개변수가 없는 함수의 경우 void를 굳이 기입하지 않아도 되며, 기입하는 관습은 옛 관습이니 굳이 지키지 않아도 된다. 실무에서 그 방식으로 작성한다면 따르도록 하자.

 

// 매개변수 void 넣는 것은 옛날 방식
void my_function(void)
{

}

int main()
{
	//void는 메모리를 차지하지 않기 떄문에 선언할 수 없다.
	//void my_void;
	int i = 123;
	float f = 123.456f;

	void* my_void;

	// 데이터 타입이 다르고 사이즈가 달라도 해당 데이터의 주소를 표현하는 데이터의 크기는 동일하다.
	my_void = (void*)&i;
	my_void = (void*)&f;

	return 0;
}

 

void는 메모리를 차지하지 않기 때문에 선언할 수 없다. 하지만 포인터로는 활용이 가능한데, 데이터 타입이 다르고 사이즈가 달라도 데이터에 대한 주소를 표현하는 데이터의 크기는 언제나 동일하므로 가능하다. 추후 포인터에서 다루게 되면 더 자세히 포스팅하도록 하겠다.

+ Recent posts