배움 저장소

[홍정모의 따라하며 배우는 C++] 3. 연산자들 본문

Programming Language/C++

[홍정모의 따라하며 배우는 C++] 3. 연산자들

시옷지읏 2021. 12. 14. 01:42

3.1 연산자 우선순위와 결합 법칙


연산자 Associativity(우선순위)

- 연산자 우선순위는 다음을 참고하자 https://en.cppreference.com/w/cpp/language/operator_precedence

 

C++ Operator Precedence - cppreference.com

The following table lists the precedence and associativity of C++ operators. Operators are listed top to bottom, in descending precedence. ↑ The operand of sizeof can't be a C-style type cast: the expression sizeof (int) * p is unambiguously interpreted

en.cppreference.com

 

예제

assignmnet operator를 사용하면 오른쪽이 먼저 계산된다

// calculate right side first. then assign to left
int x = (1-2) + (-3);

 

&&는 ||보다 우선순위가 높다. associativity는 Left-to-Right

- (true or false) and (false or false)을 의도하여 아래 코드를 작성하였다. 의도한 바를 따랐다면 false가 나와야한다.

결과값은 true가 출력된다. &&가 우선순위가 높기 때문이다. 위 의도를 코드로 구현하려면 ( )를 활용해주자.

bool result = true || false && false || false;
				
cout << boolalpha;
cout << result << endl; // >> true

 

pow( ) 함수 만들어보기

/* 
    Function to calculate x raised to the power y in O(logn)
    Time Complexity of optimized solution: O(logn)
*/
int power(int x, unsigned int y){
    int temp;
    if (y == 0)
        return 1;
                                // pow(3, 5) = 3 * pow(3, 2) * pow(3, 2)
    temp = power(x, y / 2);     // pow(3, 2) =     pow(3, 1) * pow(3, 1)
    if ((y % 2) == 0)
        return temp * temp;     // pow(2, 4) = pow(2, 2) * pow(2, 2) 
    else                        // pow(2, 2) = pow(2, 1) * pow(2, 1)
        return x * temp * temp;
}

 

3.2 산술 연산자 arithmetic operators


음수와 '-' unary operator를 사용하여 양수를 표현한다

cout << (1 - -1) << endl; // >> 2

 

아래 결과값은 -2.5이다. 이를 -2로 표기할까 -3으로 표기할까?

- C++11 표준은 음수와 나누기 연산자를 사용할 때 나머지 값을 버린다.

cout << -5 / 2 << endl;   // >> -2

 

나머지 연산자를 사용해보자. 피연산자의 부호가 다를 때 어떤 부호를 써야할까? 

- C++11 표준은 첫 번째 피연산자의 부호를 따라간다.

cout << -5 % 2 << endl;  // >> -1
cout << 5 % -2 << endl;  // >>  1

 

3.3 증감 연산자 increment decrement operators


증감 연산자를 잘못 사용한 예

(1) 아래 add( ) 함수의 출력값은 4이다. 컴파일러마다 달라질 수 있다. 같은 주소값을 공유하는 두 변수를 Parameter(매개변수)로 사용한다면 증감 연산자를 쓰지말자.

int add(int a, int b){
	return a + b;
}

int main(){
	int x = 1;
	cout << add(x,++x) << endl; // >> 4
}

 

(2) 아래 코드는 Undefined Behavior에 해당한다. 다음과 같이 쓰지 말자.

int x = 1;
x = x++;

cout << x << endl;

 

증감연산자에 값을 할당해보자.

 전위 연산자와 변수는 값을 할당할 수 있지만 후위연산자와 변수는 값을 할당할 수 없다.

int x = 0;
++x = 2;   // Wrok!
//x++ = 2; // Error

 

후위 연산자가 작동하는 원리

(1) 임시 객체를 만들고 기존 값을 복사하여 대입한다.
(2) 사본을 반환한다.
(3) 원본을 1증가시킨다.

따라서 x++는 임시로 할당받은 값이다. R-Value이다.

 

전위연산자가 작동하는 원리

(1) 원본 값에 1을 더하여 반환한다.

따라서 ++x는 원본이며 L-value이다. 


3.4 sizeof, 쉼표 연산자, 조건부 연산자


Sizeof

- sizeof는 Operator(연산자)이다. 함수가 아니다. 변수명과 사용하면 parentheses"( )"없이 작동한다.

double d;
cout << sizeof d;

 

Comma Operator

ㄴ "comma with parenthese"

int x = 0;
int y = 10;

int z = (++x, ++y);
// above code are same as
// {
//   ++x;
//   ++y;
//   z = y;
// }

cout << x << " " << y << " " << z << endl; // >> 1, 11, 11

 

  ㄴ"comma with assignment"

int a = 1, b = 10;
int c;

/* assignment operator has prior precedence than comma */
c = a, b;
// same as (c = a), b;

cout << c << endl; // >> 1

 

Conditional Operator(arithmetric if)

- 3항연산자: const variable(상수)를 조건에 맞추어 초기화할 때 유용하다. runtime constant를 이용한다

bool onSale = true;
const int price = (onSale) ? 10 : 100;

아래 코드보다 효율적이다. 조건과 true/false 값이 모두 간단할 때 사용하자.

int getPrice(bool onSale)
{
    if(onSale)
        return 10;
    else
        return 100;
}

const int price_f = getPrice(true);

 

3.5 관계 연산자 Relational Operators


double 자료형의 한계

- double은 정밀한 숫자를 표현할 수 있는 자료형이지만 한계가 있다

- 이때 사용한 setprecision은 <iomanip>에 포함되어 있다.

double d1(100 - 99.9);
double d2(10 - 9.9);
cout << std::setprecision(16);
cout << d1 << endl;      // >> 0.09999999999999432
cout << d2 << endl;      // >> 0.09999999999999964
cout << d1 - d2 << endl; // >> -5.329070518200751e-15

 

double 자료형을 어림하여 비교하기

- 오차의 최대값을 설정하고(epsilon) 오차의 최대값을 기준으로 두 수가 같은지를 비교할 수 있다.

- 수치해석 과목을 들으면 이와 관련된 최적화 방법을 배울 수 있다. 

 - abs는 <cmath>에 포함되어 있다.

double d1(100 - 99.9);
double d2(10 - 9.9);

// epsilon choose whether two double is same or not
const double epsilon = 1e-10; 
if (std::abs(d1 - d2) < epsilon)
    cout << "Approximatly same variable" << endl;
else
    cout << "two variable is different" << endl;

참고) nan, +-inf 값이 나올 경우 관계연산자는 제대로 작동하지 않는다.

 

3.6 논리 연산자 logical operators


XOR Operator

- C++에서 XOR Operator는 구현되어 있지 않다. XOR은 두 값이 같으면 false 다르면 true를 반환한다. 다음 코드로 대신하여 쓸 수 있다. " != "

x != y

 

논리연산자의 우선순위(Precedence)

- 아래 코드를 보고 결과값을 예측해보자. Negation Operator( ! )가 Equal to Operator(==) 보다 우선순위이다.

bool T = true;
bool F = false;
if (!T == F) // (!T) == F 
    cout << "Yes" << endl; // >> Yes
else        // !(T==F)
    cout << "No" << endl;

  위 if expression을 아래처럼 씀이 현명하다.

if(T!=F)

 

short circuit evaluation

(1) 아래 코드 if-조건문을 보자. (left expression) && ( right expression )이 모두 true일 때 y값은 3이다.

int x = 1;
int y = 2;
if (x == 1 && y++ == 2){
    cout << "Of course" << endl;
}
cout << y << endl;
>> Of course
>> 3

 

(left expression) && (right expression)이 false && ???? 일 때

(2) if-조건문에서 (left expression)이 false가 되면 (right expression)은 실행하지 않고 넘어감을 확인할 수 있다.

int x = 2;
int y = 2;
if (x == 1 && y++ == 2)
{
    cout << "Of course" << endl;
}
cout << y << endl;
>> 2

 

De Morgan's Law

- 논리연산자 계산에서 분배법칙을 구현해보자. 논리연산자에서 분배법칙을 적용할 수 없다.

bool x = true, y = false;

cout << std::boolalpha;
cout << ( !(x && y) ) << endl;
cout << (  !x && !y ) << endl;
>> true
>> false

분배법칙을 직접 구현해 적용하려면 논리 연산자도 뒤집어주어야 한다.

cout << ( !(x && y) ) << endl;
cout << (  !x || !y ) << endl;
cout << ( !(x || y) ) << endl;
cout << (  !x && !y ) << endl;

 

&&와 ||의 우선순위

- &&는 || 보다 우선 순위이다. 첫 출력값을 false라 예측하기 쉽다. 되도록이면 ( )를 써서 표시하자

cout << std::boolalpha;
cout << (   true || false && false  ) << endl;
cout << (  (true || false) && false) << endl;
>> true
>> false

 

3.7 이진수 Binary Numbers


Decimal to Binary

decimal 148을 binary로 바꾸어보자.

 

 ㄴ나머지로 찾기

148 / 2 = 74 ... 0 <- the smallest bit
74  / 2 = 37 ... 0
37  / 2 = 18 ... 1
18  / 2 =  9 ... 0
9   / 2 =  4 ... 1
4   / 2 =  2 ... 0
2   / 2 =  1 ... 0 
1   / 2 =  0 ... 1  <- the largest bit
                 1001'0100 = 148

 ㄴ해당 숫자를 넘지 않는 2의 지수 찾기 

1 2 4 8 16 32 64 128 256 512 1024 ...

(1) find the largest number but smaller than target num
(2) extract largests num to target
(3) do 1. again
(4) if target is 0, add all the (1.) number
----------------
target = 148
128 = 2^7
148 - 128 = 20
----------------
target = 20
16 = 2^4
20 - 16 = 4
----------------
target = 4
4 = 2^2
4 - 4 = 0
----------------

2^7 + 2^4 + 2^2 
1001'0100 = 148

 

2의 보수 표현법으로 음수 나타내기

참고 자료 https://hoplite.tistory.com/44

 

부호가 있는 이진수 1000은 0일까?

Why is 1000 = -8 in signed 4-bit binary? I was wondering why 1000 in signed 4-bit binary notation is equal to -8 in decimal. From my understanding, the 1 simply states that sign (1 being negative an..

hoplite.tistory.com

 

33의 음수를 2의 보수법으로 표현해보자.

(1) 33을 binary로 나타낸다.

(2) 1과 0을 모두 반대로 뒤집는다.

(3) 위 에서 만든 binary 값에 1을 더한다.

printf("%d\n", 0b00000000'00000000'00000000'00100001); // reverse 0 and 1
printf("%d\n", 0b11111111'11111111'11111111'11011110); // 2's complement
printf("%d\n", 0b11111111'11111111'11111111'11011111); // above value +1 = negative val
printf("%d\n", 0b11111111'11111111'11111111'11111111); // -1
>>  33
>> -34
>> -33
>>  -1

 

signed vs unsigned

- signed의 첫 번째 bit는 부호를 나타내는 수로 2^32가 아니다. 따라서 (signed 값을 unsigned 값으로) 혹은 (unsigned 값을 signed)로 변환하면 완전히 다른 값이 된다.

 

 

3.8 비트단위 연산자 Bitwise Operators


Binary digit을 출력을 도와주는 <bitset>

cout << std::bitset<3>(1) << endl; // 001
cout << std::bitset<3>(3) << endl; // 011
cout << std::bitset<3>(5) << endl; // 101
cout << std::bitset<3>(7) << endl; // 111
>> 001
>> 011
>> 101
>> 111

 

Bitwise Operator

- 자료형의 최소 크기는 (bool) 1byte이다. bitwise Operator를 사용하여 저장공간을 아낄 수 있다. 속도가 빠르다.

https://hoplite.tistory.com/82

 

[C] 홍정모의 따라하며 배우는 C언어 15 비트다루기

15.1 비트단위 논리 연산자 비트단위 논리 연산자는 낱개로 사용한다. C언어에서 거듭제곱 연산자(^)를 지원하지 않는다. ^연산자는 bitwise에서만 사용한다. Bit 연산자의 활용 bitwise 연산을 통해

hoplite.tistory.com

 

bitwise operator를 사용할 때 unsigned 자료형을 사용한다

- signed 자료형이 음수를 가리킬 때 right shift operator(>>)는 새로운 공간을 1로 체운다.

위 링크에 "15.6 비트단위 쉬프트 연산자 (비트 이동 연산자) - 부호를 가지는 정수는 자리 이동을 하면 어떻게 될까? " https://hoplite.tistory.com/82 를 참고하자

 

left - right operator는 곱하기 연산보다 빠르다

- 2의 지수를 곱셈/나눗셈 할 때  <</>>를 사용하자

 

3.9 비트 플래그, 비트 마스크 사용법 Bit flags, Bit masks


Bit에 아이템 정보를 저장하여 활용하기

unsigned char items_flag = 0;
cout << bitset<8>(items_flag) << endl;

/* get item located in first bit */
items_flag |= 0b0000'0001;
cout << "get  1  "<<bitset<8>(items_flag) << endl;

/* get item located in second bit */
items_flag |= 0b0000'0001 << 1;
//items_flag |= 0b0000'0100;
cout << "get  2  "<<bitset<8>(items_flag) << endl;

/* get item located in 5 and 6 bit */
items_flag |= (0b0001'0000 | 0b0010'0000);
cout << "get 5,6 " << bitset<8>(items_flag) << endl;

/* checking has first item? */
if( items_flag & (0b0000'0001) )
    cout << "First item is On" << endl;
else
    cout << "First item is Off" << endl;

/* lost first item */
items_flag = items_flag & (~0b0000'0001);
cout << "lost 1  "<<bitset<8>(items_flag) << endl;

/* checking has first item? */
if (items_flag & (0b0000'0001))
    cout << "First item is On" << endl;
else
    cout << "First item is Off" << endl;

/* is 4 off and is 5 on? then switching*/
if (!(items_flag & 0b0000'1000) && (items_flag & 0b0001'0000))
    items_flag ^= 0b0000'1000 | 0b0001'0000;
cout << "cvrt4-5 " << bitset<8>(items_flag) << endl;

 이때 delete: flag   = flag & ~target_bit는 해당 비트를 off.

       toggle: flag ^= target_bit, 서로 다른 값을 가지는 bit만 on.

 

Bitmask를 활용하여 RGB값을 각각으로 분리해보자

unsigned char R, G, B;

//                             RRGGBB
unsigned int pixel_color = 0x00DAA520;
// red   mask              0x  FF
// green mask              0x    FF
// blue  mask              0x      FF
cout << "pixel colr = " << bitset<32>(0x00DAA520) << endl;

// FF = 1111 ' 1111
cout << "red   mask = " << bitset<32>(0x00FF0000) << endl;
cout << "green mask = " << bitset<32>(0x0000FF00) << endl;
cout << "blue  mask = " << bitset<32>(0x000000FF) << endl;

/* Extrack blue color */
cout << "blue color = " << bitset<32>( pixel_color & 0x000000FF ) << endl;
cout << "blue value = " << (pixel_color & 0x000000FF) << endl;

cout << "grn  color = " << bitset<32>( (pixel_color & 0x0000FF00) >> 8 ) << endl;
cout << "grn  value = " << ((pixel_color & 0x0000FF00) >> 8) << endl;

cout << "red  color = " << bitset<32>((pixel_color & 0x00FF0000) >> 16) << endl;
cout << "red  value = " << ((pixel_color & 0x00FF0000) >> 16) << endl;
Comments