Chapter 3. Dealing with Data

지환·2022년 6월 2일
0
이제부터 C랑 겹치는 내용이 좀 나오지싶은데,
잘 알고있는거라 굳이 언급할만한게 아니면 따로 정리하진 않아야지

OOP의 본질은 나만의 data type을 만들고 확장하는 것이다.
하지만 그 전에 기본 built-in data type을 알아야 한다.

built-in types은 두 종류로 나뉜다.
1. fundamental types (이 chapter에서 다룸)
2. compound types (array, string, pointer, structure 등 chapter 4에서 다룸)


Simple Variables

정보를 저장하기 위해 알아야하는 특성
1. 어디 저장했는지
2. 어떤 값이 저장됐는지
3. 어떤 종류의 informantion이 저장됐는지(크기때문에 그런듯)

int braincount = 5;
라는 statement에선, int형을 저장하기 충분한 공간을 memory에 할당하고, 그 위치를 기억하고, value 5를 그 location에 저장하는 과정이 일어난다.
(별거 아닌거같은데 어셈블리어 좀 배우고 보니 색다르게? 보이네)
(이 statement가 어디에 저장됐는지 우리에게 알려주진 않지만, program이 알아서 keep track)

Names for Variables

길어도 괜찮으니 좀 의미있는 이름을 지어라 (C++은 길이에 제한을 두지 않는다. 물론 platform에 따라 제한이 있을 수 있음)
(ANSI C에선 63개까지만 구분되도록 했었음)

naming rule은 C와 같음(문자, 숫자, underscore 올 수 있고, 숫자는 처음에 못온다.)
추가로, 'underscore 2개(__)'로 시작하거나 'underscore 1개+대문자' 는 implementation에서 사용하도록 예약돼있다.
'underscore 1개(_)'로 시작하는건 implementation이 global identifier로 사용하도록 예약돼있다.
(마지막 추가로 말한 규칙들은 지키지 않아도 compiler error는 아니지만(naming 규칙을 지켰으므로 error는 아닌 것이다, implementation이 예약을 한게 문제가 되는거지) UB이다.)

(naming scheme은 말이 많은데, 그냥 본인 style에 맞게 정확하고 일관되게 기술하면 된다.
type이나 contents를 이름에 적어주기도 한다. ex.int nMyWeight or int intMyWeight)

Interger Types

char, short, int, long, long long(C++11)
signed or unsigned

The short, int, and long long Integer Types

모든 computer designs을 만족하는 type들의 따른 특정 bit수가 없기때문에, standard에선 C처럼 minimum size만 정의한다.

  • short는 최소 16 bits
  • intshort보다 크거나 같다.
  • long은 최소 32 bits이고, int보다 크거나 같다.
  • long long은 최소 64 bits이고, long보다 크거나 같다.

Unsigned Types

unsigned만 있으면 unsigned int
unsigned는 값 범위 넘어가면 다시 돌아가는걸 보장하지만, signed가 초과할 수 있도록 보장하진 않는다.

Bits and Bytes
computer memory의 기본 unit은 두말할 필요없이 bit이다.
byte는 주로 memory의 8-bit unit을 말한다.
하지만 이는 memory 크기 얘기할때고,
C++에서 byte해당 implementation의 basic character set을 충족시킬 수 있는 최소 bit 수이다.
그리고 char는 해당 bit 수를 가지도록 한다.
미국에선 ASCII나 EBCDIC set을 주로 쓰는데, 얘네는 8bits로 모두 만들 수 있다. 그래서 C++에서도 byte는 주로 8 bits이긴 하다.
하지만 international programming에서 unicode 같은거 쓰게되면 16-bit byte나 32-bit byte가 될 수도 있다.
(몇몇은 8-bit byte를 아예 "octet"이라고 부름)

climits 혹은 limits.h 헤더를 이용해 int형 type의 범위를 알 수 있다.
CHAR_BIT를 통해 byte가 몇개의 bit로 이루어졌는지 알 수 있다.

>> The sizeof Operator and the climits Header File <<

sizeof operator 사용할때 type name이면 괄호 필수이지만, variable name이면 괄호는 선택이다.
sizeof (int)
sizeof n_short : n_short는 variable name

#define으로 symbolic constants를 지정하는건 C relic이고,
C++에서는 주로 const를 이용해 symbolic constants를 만들기때문에 #define을 많이 쓸일은 없다.
(const에 관해선 뒤에 얘기. C랑은 좀 다르게 처리되나봄)

Initialization (C++11 포함)

int owls = 101; : traditional C initialization
int wrens (432); : C++ syntax (C에선 적용안됨)

초기값 알고있다면 initialize 하는게 좋다.

C++98
int hamburgers = {24};
이렇게 array나 structure에 쓰이던 initializer형태를 일반 변수에도 쓸 수 있게 함.

C++11
int emus{7};
int rheas = {12};
int rocs = {}; //0으로 set
int dls{} //0으로 set
이렇게 = 기호 없이도 작동하도록 했다.
참고로 이 기호를 사용하면 narrow assignment 금지

왜 이것저것 표현 방법을 추가했지?
novice가 쉽게 사용하도록 하기 위해서이다.
원래 C++은 initialization 형태가 class variable에 적용할때랑 일반적인 곳에 적용할때랑 달랐다.
이를 비슷하게 하기위해 일반 변수에도 괄호를 사용할 수 있도록 했고,
C++11에선 모든 type에 brace syntax를 사용할 수 잇도록 했다.

Choosing an Integer Type

대게 int는 해당 computer에서 가장 자연스러운 integer size로 set된다.
가장 효율적으로 다룰 수 있단 소리다.
따라서 별다른 이유 없으면 int쓰면 된다.

큰 숫자면 `long`으로 처리해도 좋다.
왜냐하면 내 컴퓨터에서는 `int`에 넣을 수 있더라도, 다른 컴퓨터로 옮겼을 경우도 생각해야한다.
같은 이유로, 공간절약이 중요하다면 내 컴퓨터에서 `short`와 `int` size가 같더라도 `short`를 써야한다.

이건 source code 단계에서 하는 얘기지 싶네
실행파일은 다른 bit에선 안돌아가니까(다른 OS면 당연히 더 안돌아가고)

Integer Literals

integer literal 표현법
첫번째 digit이 0-9면 base 10
첫번째 digit이 0이고, 두번째 digit이 1-7이면 base 8
처음 두 digit이 0x, 0X이면 base 16

얘네는 그냥 편의를 위해 표현만 다르다는걸 알고있어야한다. 컴퓨터엔 (같은 수는) 2진수로 저장된다.

cout은 기본적으로 decimal form으로 출력
hex, oct manipulator를 이용해 출력 form을 변경할 수 있다.
cout << hex라고 하면 이 다음부터 숫자는 hexadecimal로 출력된다.

std namespace를 사용한다고 적어뒀으면 hex를 변수 이름으로 사용하지 못한다.
그러지않고 std::hex 형식으로 사용하면 hex를 변수 이름으로 사용해도 괜찮다.

Constant의 type은?

constant는 기본적으로 int에 저장

suffix가 있는 경우,
그에 맞는 type에 저장(문자 l suffix는 숫자 1로 보일 수 있으니 L 권장)

size가 int에 맞지 않는 경우(without suffix),
10진수로 표현된 숫자라면 int, long, long long의 순서로 들어갈 수 있는 가장 작은 type에 들어가고,
8진수나 16진수라면 int, unsigned int, long, unsigned long, long long, unsigned long long의 순서로 들어갈 수 있는 가장 작은 type에 들어간다.

10진수와 8/16진수를 저장하는데 차이가 있는 이유는,
8/16진수는 주로 memory address를 표현하기 때문이다.
주소를 표현하는데는 unsigned가 적합하므로 저장 방식에 차이가 있다.
16-bit address를 표현하는데 long보단 unsigned int가 더 적절하다.

The char Type

(Characters and Small Integers)

문자도 결국 숫자로 변환돼 저장되므로 char type은 또 다른 integer type이다.
(그러므로 short보다 작은 숫자를 다룰거면 char 이용해도 됨. 물론 당연히 character 다루는데 더 많이 쓰이긴 함.)

host system이 사용하는 character set을 사용한다. 예를들어 IBM mainframe은 EBCDIC("eb-se-dik"이라 발음)

int랑 다르게 default가 signed인지 unsigned인지 정해진게 없다.
implementation마다 다르므로 이게 중요하다면(숫자로 사용하거나 한다면) 정확히 명시해라.

char ch;

cin >> ch;
cout << ch;

를 하면 ch는 문자 "형태"로 입력되고 출력된다.
char type이면 cincout이 그렇게 되도록 기능하는 것이다.
int면 숫자 그대로 출력됨, type에 따라 cout이 display style을 결정한다.

마찬가지로 cin도, cin >> ch에서 ch가 int라고 해보자.
5라고 입력하면 숫자 5가 그대로 들어가지만,
ch가 char이면, 실제론 숫자 53이 저장되는 것이다.

cout.put(ch); : single character ch를 출력한다.

A Member Function: cout.put()

cout.put()member function이다. (OOP 개념)
class는 data를 (1)어떻게 표현할지, (2)어떻게 다룰지 를 정의하는데,
member 함수는 class에 속해서 class data를 (2)어떻게 다룰지 를 정의(?)한 것이다.

ostream class에는 put()이라는 member function이 있는 것이고, 이 함수를 해당 class의 object인 cout에 사용하는 것이다.
이때 membership operator라고 불리는 period(.)를 같이 사용해줘야 한다.

  • cout.put() 필요있나?
    이런 중복되는게 왜 있나 찾아보면 대부분 그렇듯이 historical reason인데,
    C++ 2.0 이전에 cout은 character variable은 문자로 잘 출력했지만, character constant는 숫자로 출력했다.(cout << 'M';이라고 하면 숫자로 출력됐단 소리)
    왜냐하면 초기 C++은 C와 같이 character constant를 int에 저장했기때문이다.
    (실제로 sizeof 연산해보면 C에선 4바이트, C++에선 1바이트로 나옴)
    character constant와 다르게 char variable은 8bit에 잘 저장됐기때문에,
    cout이 character variable과 constant를 다르게 봐서 생긴 문제이다.(현재는 single-character constant를 int가 아닌 char에 잘 저장한다.)
    그래서 그때는 charcter constant를 출력하려면 cout.put('M');의 형태로 출력해야했다.
    그게 남아 있는 것

char Literals

char Literal을 표현하는 방법

  1. 'a' 이런 식으로 single quote 안에 문자를 넣어서 표현
    : numeric code가 달라도 적용된다.(ASCII와 EBCDIC의 code는 다르지만 저렇게 표현하면 두 군데서 적용 가능)

  2. keyboard로 표현할 수 없는 문자 혹은 표현되더라도 특별한 의미가 있는 문자는 escape sequence code(p.85 Table 3.2)를 사용해서 표현
    : 일반 문자인듯이 사용한다. 얘도 마찬가지로 numeric code 달라도 적용된다.
    escape sequence는 teletype을 사용하던때에 만들어진거라 요즘 컴퓨터에선 지원이 잘 안될수도 있다.
    (예를들어 alarm character를 줘도 system이 조용하다거나)
    문자말고 해당 character set의 8/16진수 코드에 맞게 작성해도 된다.

    numeric/symbolic escape sequnce 중 뭐가 더 낫나?
    : 선택할 수 있다면, symbolic code를 써라.
    numeric은 특정 character set에만 특정되지만, symbolic은 그렇지도않고 읽기도 쉽다.

ex)

cout << "Enter your code: _____\b\b\b\b\b";
cin >> code;

이런 식으로 \b를 이용하면 저 부분이 지워지면서 입력됨.(system마다 다르게 작동하긴함. \b해버리면 커서만 이동하는게 아니라 underscore가 지워지기도하고)

Universal Character Names

basic source character set : source code 내에서 쓰일 수 있는 character들 (영문, 숫자, {, = 같은 기호 등)
basic execution character set : program 실행 중에 사용될 수 있는 character들 (위 set에 backspace나 alert가 추가됨)

C++ 표준에선 extended source character set이나 extended execution character set을 implementation이 제공하도록 허용
그래서 국가마다 다양한 chracter set이 추가될 수 있는데, C++에선 특정 keyboard와 독립적으로 쓰일 수 있게 universal character name을 제공한다.

universal character name
\u 뒤에 4개의 16진수 digit
\U 뒤에 8개의 16진수 digit
둘 중 하나로 표현한다.

extended character set을 지원하는 implementation이라면 universal character name을 identifiers, character constants, strings에서 쓸 수 있다.

universal charcter "code"가 아니라 "name"이라고 부른다.
\u00F6 이라고 적으면 이 코드 그대로 저장되느게 아니기때문이다.
encoding에 따라 내부에 저장되는건 달라질 수 있다.
그래서 같은 name을 쓰면 다른 encoding 방식이여도 잘 작동하는 것이다.

wchar_t

C++은 8-bit에 들어맞지 않는 large character set을 여러 방법으로 다룬다.
1. large set이 해당 implementation의 basic character set이라면, compiler 제작자가 char을 16-bit byte 이상으로 설계할 수 있다.
2. implmentation이 small basic set과 large extended set을 지원한다면, 8-bit char이 basic character set을 표현하고, wchar_t type으로 extended character set을 표현한다.

wchar_t
해당 시스템의 가장 큰 extended character set을 표현하기 충분한 공간
integer type 중 하나와 같은 크기를 가진다: 그렇게 바탕이 되는 type을 underlying type 이라고 함.
char처럼 signed나 unsigned로 지정하진 못한다.

cin이나 coutchar에 맞춰져있어서 wchar_t에 대해선 제대로 기능하지 않는다.
그래서 wcin, wcout을 써야함.
wide-character constant나 string : prefix L

char16_t and char32_t

(new C++11 types)
(wchar_t만으론 충분하지 않다는게 시간 지나며 확인됨)

정해진 size와 signedness가 있으면 더 유용한 경우가 있다.
이를 위해 char16_tchar32_t를 만듦(wchar_t는 implementation마다 다르니)
둘 다 unsigned이고 bit 수는 뒤에 숫자

char16_t character나 string constant를 나타내기 위해 u prefix 사용
char32_t character나 stirng constant를 나타내기 위해 U prefix 사용

둘 다 underlying type을 가진다.
(wchar_t와 다른 점은 얘넨 bit수와 unsigned가 정해져있으니 그거에 따라 맞는 type이 underlying type이 되겠지)

The bool Type

C++은 nonzero는 true로 zero는 false로 해석

bool type
true predefined literal
false predefined literal

true, false도 int같은 numeric value로 각각 0, 1로 바뀔 수 있고,
반대로 numeric이나 pointer value가 bool로 바뀔 수도 있다.

The const Qualifier

const qualifier를 사용할때 보통 첫글자를 대문자로하거나 모두 대문자로 하기도하고, 아니면 앞에 문자 k를 붙이기도한다.

값 수정이 안되므로, initialize 필요 (static 과는 다르게 변수로 초기화 가능)

#define 방식보다 낫다.(책에서 계속 define대신 const 쓰라고 하네)
1. type 정의 가능
2. scoping rule 따른다
3. array나 structure 같은 정교한 type에도 적용 가능

const의 scope rule에서 ansi C와 차이가 있다는데 그건 chapter9에서 설명
그리고 C++의 const 변수는 배열 길이 명시하는데 사용될 수 있다고 한다.
(C도 VLA로 치면 할 순 있는데, 얘는 아예 일반적인 배열 말하는듯.)
(아래 첫번째 link 보니 C++에선 const 사용시 아예 constant expression임)

C와 C++ const 차이점
C와 C++ const 처리 차이점
C++엔 VLA가 없구만,, 그 이유

Floating-Point Numbers

(integer에 비해 훨씬 크거나 작은 수 표현 가능)

Floating-Point Numbers 표현 방식

(1) decimal point : I/O할때는 지역에 맞는 decimal point 사용해도 OK, code내에선 무조건 .으로 써야함.
(2) E notation : 숫자랑 띄어쓰면 안됨. 8.33E-4 식으로 작은 수도 표현

Floating-Point Types

float
doubl
long double

Floating-Point Constants

floating-point constant는 기본적으로 double
float -> f, F suffix
long double -> l, L suffix

Floating-Point Numbers의 장단점

integer보단 크고 작은 수 표현할 수 있지만,
integer operation에 비해 좀 느리다.

precision을 넘어가서 표현할 순 없다.(구분이 안됨)

floating-point type과 integral(integer) type 전체를 arithmetic type이라고 함.
floating-point type에는 float, double, long double이 있고,
integral(integer) type에는 signed integer type과 unsigned integer type, 그리고 bool, char, wchar_t가 있다.
signed와 unsigned integer type은 뭐 말안해도 알테니..

C++ Arithmetic Operators

초등학교때 4칙연산이 쉬웠던 것처럼, 컴퓨터에게도 간단한 연산들이 있다.
addition, substraction, multiplication, division, modulus (어셈블리어에서도 정의된 것들이네)

modulus 연산 : 둘 다 int여야함. operands 중 하나가 negative라면 a=(a/b)b+a%ba = (a/b)* b + a\%b 를 만족하도록 결과값의 부호가 결정된다.
(int division과 함께 양을 나눠야할때 주로 쓰인다. pound 무게를 stone 단위로 바꾼다던가)

>> Order of Operation <<

한 operand에 두개의 operator가 적용되면 -> precedence
precedence가 같다면 -> associativity

두 operator가 한 operand를 공유해야만 precedence와 associativity로 해결 가능
20*5 + 24*6; 같은 경우 앞의 곱셈과 뒤의 곱셈 중 누가 먼저 계산될지 모른다.
(implementation defined)

Division Diversions

operands 종류에 따라 divison operator도 종류와 행동이 나뉜다.
int division, float division, double division, ...
(어셈에서 fdiv, idiv, div 연산이 다 따로있다. 얘네가 그걸 그대로 쓰는진 모르겠으나..)

이렇게 같은 symbol로 하나 이상의 operation을 표현하는걸 operator overloading이라고 한다.
(중요한 OOP 특징. operator overloading이 OOP에만 있는건 아니지만, user defined class에서도 operator overloading 허용하기때문에 중요 특징이라는거 같다.)

Type Conversions

automatic type conversion이 일어나는 경우
1. assign
2. expression
3. argument
type이 맞지 않을때 (C에선 return type인 경우도 그랬는데, 얘도 나중에 설명하려나)

(1) Assignment

더 큰 range로 assign되는건 주로 잘 된다. 아래의 경우는 문제가 생길 수 있다.(이외에도 뭐 signed unsigned 라거나.. 안전하게 하자 그냥)

Conversion TypePotential Problems
큰 floating type을 작은 floating type에precision을 잃을 수 있다. range를 벗어나면 Undefined
Floating type을 integer type에소숫점 뒷부분 잃는다. range를 벗어나면 Undefined
큰 integer type을 작은 integer type에범위를 벗어날 수 있다. 그러면 주로 low-order byte만 복사된다
유효숫자가 많은 integer를 floting type에유효숫자 개수가 precision을 넘어가면, 정확하게 저장되지 않을 수 있다.

(마지막 경우, float이 숫자 범위는 int보다 더 큰값을 나타낼 수 있지만 유효숫자 부분은 잘 고려해야됨)

bool에 assign할때 0은 false로, nonzero는 true로 convert됨

>> {} 썼을때 conversion (C++11) <<

C++11에선 {}를 사용한 initialization을 list-initialization이라고 한다.

()를 쓰거나 그냥 일반적으로 assign할때는 위처럼 되지만, {}를 쓰면 좀 더 제한적이다.
narrowing 금지. assigned value를 나타낼 수 없으면 안된다.

ex.
floating type -> integer type : X (소숫점부분이 0이거나 해도 안된다.)
'변수'도 얼마나 큰지 당장 알 수 없으므로 narrow를 판단할 수 없어서 일단 char c = {x}; 꼴도 안된다고(error) 함.(단, x가 const로 선언됐으면 가능)
(해보니 또 잘 되긴하는데 표준 찾아보니 안된다고 정의돼있다.)

(2) Expression

  1. 몇몇 type은 일단 자동 변환된다. integral promotion
    bool, char, unsigned char, signed char, short -> int
    (intshort보다 크다면) unsigned short -> unsgined int
    wchar_t -> int, unsigned int, long, unsigned long (이 중 모든 범위를 수용할 수 있는 가장 작은 곳에 들어감)
    ex) short c = a+b; //a와 b는 short : 이걸 실행하면 a와 b가 int로 바꼈다가 계산 후 다시 short로 바뀌는 것이다.
    좀 돌아가는 것 같지만, int는 해당 computer에 가장 자연스러운 가장 빠르게 계산할 수 있는 type이므로 이게 맞다.

  2. 나머지들은 다른 type과 expression으로 묶이면 변환된다.
    이땐 작은 것이 큰 것으로 변환된다. (자세한 변환 방식 list는 p. 106에 있음. rank 관련 부분은 좀 아래에 표시해둠)

(3) Argument

prototype을 통해 type을 체크

Type Casts

(long) a;
long (a);
두가지 형식이 있음. (C에선 전자만 허용)
아래는 함수호출같이 보이게 하는 효과

Stroustrup(C++ 창시자)님께서 기존 C의 type cast는 제한사항이 딱히 없어 위험하다고 생각
그래서 다음과 같은 type casting operator 추가(기존 casting보단 제한적임)
static_cast<typeName> (value)

auto Declarations (in C++11)

compiler가 initialization value의 type을 추론하도록하기위해 auto를 쓴다.

본래 C에서 auto는 storage class를 정의하기 위한 것으로,
block 내부에서 선언된 variable에만 쓰일 수 있었다.
auto를 적용하면 automatic storage duration, block scope, no linkage가 되는데,
이미 block내부에서 선언된 variable은 해당 특징을 가지므로 auto는 쓰일 일이 거의 없었다.

그래서 이 의미를 redefine한 것이다.

auto x = 1.5; 하면 x는 자동으로 double이 됨.
잘못쓰일 수 있으니 조심해야한다. ex) double을 의도했는데, auto x = 0;라고 할 수 있음.

STL의 복잡한 type 다룰때 유용

C++11 이후론 이전의 의미로는 사용하지 못한다.
standard를 만들때 새로운 keyword를 추가하는 것은 기존 code와 호환문제로 좀 꺼려진다.
그렇기 때문에 새로운 것을 소개하기보단 기존에 있던 keyword이고 거의 쓰이지 않았던 auto를 가져온 것

Summary

C++에서 char는 system's basic character set을 모두 표현할 수 있도록, wchar_t는 system's extended character set을 모두 표현할 수 있도록 정의한다.

type도 많고, conversion rule도 복잡하고 해서 너무 과하다고 생각할 수 있지만, 다 필요한 것이다..
하다보면 알게 됨

0개의 댓글