12. 비트 연산자와 시프트 연산자로 비트 단위 연산하기

  • 55 minutes to read

이번 강의는 변수의 값을 비트 단위로 연산하는 비트 연산자와 시프트 연산자의 사용법에 대해 다룹니다.

> // 비트 연산자: & (비트단위 AND), | (비트단위 OR), ^ (비트단위 XOR), ~ (비트단위 NOT)
> // 시프트 연산자: << (왼쪽 시프트), >> (오른쪽 시프트)

이진수

다음 이진수 관련 내용은 앞서서 이미 학습했었던 중복된 내용이니, 다음 절로 넘어가도 됩니다.

이진수의 이해: 컴퓨터 언어의 기본

이진수는 모든 현대 컴퓨터 및 디지털 시스템의 기반이 되는 숫자 체계입니다. 이 아티클에서는 이진수가 무엇인지, 왜 중요한지, 그리고 어떻게 사용되는지에 대해 알아보겠습니다.

이진수란 무엇인가?

이진수는 0과 1 두 가지 숫자만을 사용하는 진법 체계입니다. 이는 10진수 체계에서 사용하는 0부터 9까지의 숫자와는 다릅니다. 이진수에서 각 자리는 '비트'(bit)라고 불리며, 각 비트는 전기적 신호의 상태를 표현할 수 있습니다—0은 꺼짐(off), 1은 켜짐(on)을 의미합니다.

이진수의 중요성

이진수는 컴퓨터 하드웨어와 소프트웨어 양쪽 모두에서 기본적인 언어로 사용됩니다. 컴퓨터는 내부적으로 이진수를 사용하여 데이터를 저장하고 처리합니다. 이러한 이유로, 모든 프로그래밍 언어와 운영 체제, 응용 프로그램은 궁극적으로 이진수를 사용하여 컴퓨터와 통신합니다.

이진수 사용 예

  1. 데이터 저장과 처리: 컴퓨터는 텍스트, 이미지, 비디오 등 모든 형태의 데이터를 이진 형식으로 저장합니다. 예를 들어, 문자는 ASCII 코드를 사용하여 이진 숫자로 변환되어 저장됩니다.

  2. 논리 연산: 컴퓨터는 이진 논리 연산(AND, OR, NOT 등)을 사용하여 계산을 수행합니다. 이러한 연산은 컴퓨터 프로세서 내의 트랜지스터 수준에서 이루어지며, 이는 모든 프로그램 실행의 기본을 형성합니다.

  3. 네트워킹: 컴퓨터 네트워크에서 데이터 전송도 이진 형태로 이루어집니다. 예를 들어, 인터넷을 통해 보내는 데이터 패킷은 이진 코드로 인코딩되어 전송됩니다.

이진수의 장점

이진수 시스템은 그 간결성과 오류 감지 및 수정이 용이하다는 점에서 매우 효율적입니다. 또한, 이진수는 물리적으로 신뢰성이 높은 전자 부품을 사용하여 쉽게 표현되고 조작될 수 있습니다.

10진수와 2진수 변환 가이드

이 아티클에서는 양수에 한정하여 10진수를 2진수로 변환하는 방법과 그 반대의 과정을 단계별로 설명하겠습니다.

10진수를 2진수로 변환하기

10진수를 2진수로 변환하는 과정은 다음과 같습니다:

  1. 나눗셈과 나머지: 주어진 10진수를 2로 나누고, 나머지를 기록합니다. 이 과정을 수가 0이 될 때까지 반복합니다.
  2. 결과 기록: 마지막 나머지부터 첫 번째 나머지까지 역순으로 기록합니다. 이렇게 하면 해당 10진수의 2진 표현이 완성됩니다.

예제: 10진수 156을 2진수로 변환하기

  • (156 / 2 = 78) 나머지 0
  • (78 / 2 = 39) 나머지 0
  • (39 / 2 = 19) 나머지 1
  • (19 / 2 = 9) 나머지 1
  • (9 / 2 = 4) 나머지 1
  • (4 / 2 = 2) 나머지 0
  • (2 / 2 = 1) 나머지 0
  • (1 / 2 = 0) 나머지 1

역순으로 나머지를 읽으면 156의 2진수는 (10011100)입니다.

2진수를 10진수로 변환하기

2진수를 10진수로 변환하는 방법은 다음과 같습니다:

  1. 가중치 적용: 2진수의 각 자리수에 가중치를 적용합니다. 가장 오른쪽 자리부터 시작하여, 각 자리의 가중치는 (2^0, 2^1, 2^2, ...)와 같이 증가합니다.
  2. 합산: 모든 자리의 가중치가 적용된 값을 합산합니다.

예제: 2진수 10011100을 10진수로 변환하기

  • (0 * 2^0 = 0)
  • (0 * 2^1 = 0)
  • (1 * 2^2 = 4)
  • (1 * 2^3 = 8)
  • (1 * 2^4 = 16)
  • (0 * 2^5 = 0)
  • (0 * 2^6 = 0)
  • (1 * 2^7 = 128)

모든 값을 합하면 (128 + 16 + 8 + 4 = 156).

음수의 2진수 표현: -45를 2진수로 변환하기

2진수는 컴퓨터가 정보를 처리하고 저장하는 기본 방식으로, 양의 정수뿐만 아니라 음의 정수를 표현하는 데에도 사용됩니다. 이 글에서는 -45라는 음수를 2진수로 변환하는 과정을 단계별로 설명하겠습니다.

1. 절댓값 구하기

음수의 2진수 변환을 시작하기 전에, 먼저 해당 숫자의 절댓값을 구해야 합니다. -45의 절댓값은 45입니다.

2. 45를 2진수로 변환하기

45를 2진수로 변환하기 위해 2로 나누는 과정을 반복하고, 각 단계에서의 나머지를 기록합니다.

  • ( 45 / 2 = 22 ) 나머지 1
  • ( 22 / 2 = 11 ) 나머지 0
  • ( 11 / 2 = 5 ) 나머지 1
  • ( 5 / 2 = 2 ) 나머지 1
  • ( 2 / 2 = 1 ) 나머지 0
  • ( 1 / 2 = 0 ) 나머지 1

이 나머지들을 거꾸로 읽으면 45의 2진수는 (101101)입니다.

3. 2의 보수 구하기

음수를 표현하기 위해서는 2의 보수를 사용합니다. 이를 구하는 과정은 먼저 1의 보수를 취한 다음, 1을 더하는 것입니다.

1의 보수는 각 비트를 반전시키는 것으로, 45의 2진수 (101101)의 1의 보수는 (010010)입니다. 이제 여기에 1을 더합니다:

  • (010010 + 1 = 010011)

4. 8비트로 확장하기

일반적으로 컴퓨터 시스템은 8비트, 16비트 등으로 비트를 표준화하여 사용합니다. 여기서는 8비트를 사용한다고 가정하고, 45의 2진수를 확장해 보겠습니다.

  • 45의 2진수를 8비트로 확장: (00101101)
  • 1의 보수 (8비트): (11010010)
  • 2의 보수 (1의 보수에 1 더하기): (11010011)

따라서, -45의 2진수 표현 (8비트 2의 보수 방식)은 (11010011)입니다.

음수 이진수를 10진수로 변환: 1101 0110을 10진수로 변환하기

이 글에서는 8비트 이진수 "1101 0110"을 예로 들어 음수 표현 방식을 단계별로 설명하겠습니다.

1. 이진수 이해하기

8비트 이진수에서 첫 번째 비트는 부호 비트로, 이는 숫자의 양수/음수 여부를 나타냅니다. 부호 비트가 0이면 양수, 1이면 음수를 의미합니다. 여기서는 "1101 0110"의 첫 비트가 1이므로 이는 음수를 표현합니다.

2. 2의 보수로 변환

음수를 10진수로 바로 변환하려면 먼저 2의 보수를 계산해야 합니다. 2의 보수를 구하는 과정은 다음과 같습니다.

1의 보수 구하기

모든 비트를 반전시켜 1의 보수를 얻습니다.

  • 원래 값: 1101 0110
  • 1의 보수: 0010 1001

2의 보수 구하기

1의 보수에 1을 더해 2의 보수를 구합니다.

  • 1의 보수: 0010 1001
  • 1 더하기: 0010 1010

3. 2의 보수를 10진수로 변환

이제 2의 보수를 10진수로 변환하여 실제 수치를 얻습니다.

  • 2의 보수: 0010 1010
    • 0 * 2^7 = 0
    • 0 * 2^6 = 0
    • 1 * 2^5 = 32
    • 0 * 2^4 = 0
    • 1 * 2^3 = 8
    • 0 * 2^2 = 0
    • 1 * 2^1 = 2
    • 0 * 2^0 = 0

합계: 32 + 8 + 2 = 42

부호 비트가 1이므로, 이 수는 -42입니다.

책읽기: 비트 연산자와 시프트 연산자로 비트 단위 연산하기 강의 소개

https://youtu.be/efi65-ib50A

1. 비트 연산자와 시프트 연산자의 종류

비트 연산자와 시프트 연산자는 2진수 비트 단위로 연산을 수행하는데 사용합니다. 우선 비트 연산자와 시프트 연산자의 6가지 종류를 표로 간단히 미리 살펴본 후 진행하겠습니다.

[표] 비트 연산자와 시프트 연산자의 종류

연산자 별칭 설명
& 비트단위 AND 비트 값이 둘 다 1일 때에만 1, 그렇지 않으면 0
| 비트단위 OR 비트 값이 하나라도 1이면 1, 그렇지 않으면 0
^ 비트단위 XOR 비트 값이 서로 다를 때에만 1, 그렇지 않으면 0
~ 비트단위 NOT 비트 값이 1이면 0, 0이면 1
<< 왼쪽 시프트 비트 값을 지정된 수만큼 왼쪽으로 이동
>> 오른쪽 시프트 비트 값을 지정된 수만큼 오른쪽으로 이동

2. 비트 연산자

비트 연산자(Bit Operator)는 정수형 데이터의 값을 이진수의 비트 단위로 연산을 수행하고자할 때 사용됩니다. 비트 연산자는 다음과 같이 4개의 기호를 사용합니다. 좀 더 구분을 짓기 위해서 영문 단어를 추가했습니다.

  • &
    • 논리곱(AND, Both)
  • |
    • 논리합(OR, Either)
  • ^
    • 배타적 논리합(XOR; Exclusive OR, Different)
  • !
    • NOT(Negation, Invert)

비트 연산자 AND, OR, XOR 간단 소개 동영상

https://youtu.be/Z19Iv9m0PLc

3. Windows 계산기를 사용하여 이진수 비트 연산자 4개에 대한 실행 결과 살펴보기

https://youtu.be/aYpD6cHqpsk

Windows OS에 내장된 계산기를 사용하면 표준, 공학용, 프로그래머용 등의 여러 가지 환경을 사용할 수 있습니다.

그림: 계산기

계산기

4. 비트 AND 연산자: & 연산자

본격적으로 비트 연산자를 사용하도록 하겠습니다. 먼저, 비트 AND 연산자인 & 연산자를 사용해 보겠습니다.

그림: 계산기로 AND 연산 수행하기

계산기로 AND 연산 수행하기

코드: bit_and.c

// & 연산자: 비트 AND 연산자(둘 다 1일때만 1)
#include <stdio.h>

int main(void)
{
   int x = 0b1010; // 10진수: 10
   int y = 0b1100; // 10진수: 12

   // x를 10진수로 표현
   printf("%d\n", x); // 10

   // y를 10진수로 표현
   printf("%d\n", y); // 12

   // x AND y를 10진수로 표현 
   printf("%d\n", x & y); // 8 

   return 0;
}
10
12
8

비트 AND 연산자인 & 연산자처럼 항을 2개 갖는 2항 연산자는 앞뒤로 공백을 두면 가독성이 좋아집니다. 즉, x%y 보다는 x % y 형태를 추천합니다.

5. 비트 OR 연산자: | 연산자

이번에는 비트 OR 연산자인 | 연산자를 사용해 보겠습니다. | 연산자는 키보드의 파이프 기호(버티컬 바)를 사용합니다.

그림: 계산기로 OR 연산 수행하기

계산기로 OR 연산 수행하기

코드: bit_or.c

// | 연산자: 비트 OR 연산자(하나라도 1이면 1)
#include <stdio.h>

int main(void)
{
   int x = 0b1010; // 10진수: 10
   int y = 0b1100; // 10진수: 12

   // x를 이진수에서 십진수로 출력
   printf("%d\n", x); // 10

   // y를 이진수에서 십진수로 출력
   printf("%d\n", y); // 12

   printf("--\n");

   // x OR y를 십진수로 표현 
   printf("%d\n", x | y); // 14

   return 0;
}
10
12
--
14

나중에 프로그래머로 일을 하다보면 이진수 처리가 중요한 분야가 있을 수 있습니다. 하지만, 처음 학습자에게는 "이런 연산자를 사용하면 이렇게 나오는구나" 정도로 알고 넘어갑니다.

6. 비트 XOR 연산자: ^ 연산자

비트 XOR 연산자인 ^ 연산자는 두 비트의 값이 서로 다를 때 1입니다. 1 ^ 00 ^ 1일 때에만 1이고 나머지는 0입니다.

그림: 계산기로 XOR 연산 수행하기

계산기로 XOR 연산 수행하기

코드: bit_xor.c

// ^ 연산자: 비트 XOR 연산자(서로 다르면 1)
#include <stdio.h>

int main(void)
{
   int x = 0b1010; // 10진수: 10
   int y = 0b1100; // 10진수: 12

   // x를 십진수로 표현
   printf("%d\n", x); // 10

   // y를 십진수로 표현
   printf("%d\n", y); // 12

   printf("--\n");

   // x XOR y를 십진수로 표현 
   printf("%2d\n", x ^ y); // 6

   return 0;
}
10
12
--
 6

^ 연산자를 포함한 몇몇 비트 및 시프트 연산자는 이번 예제 이외에는 다루지 않습니다. 그러므로 현재 예제는 가볍게 작성 후 실행해보세요.

비트 NOT 연산자: ~ 연산자

비트 NOT 연산자인 ~ 연산자는 물결(틸드) 기호를 사용합니다. ~ 연산자는 비트 값이 1이면 0으로 0이면 1로 바꿉니다. 이렇게 비트가 바뀌는 것이 비트 반전입니다.

그림: 계산기로 비트 반전

계산기로 NOT 연산 수행하기

코드: bit_not.c

// ~ 연산자: 비트 NOT 연산자(1 <-> 0, 비트 반전)
#include <stdio.h>

int main(void)
{
   int x = 0b00001010; // 10진수: 10

   // x를 십진수로 표현
   printf("~%d\n", x); // 10

   // NOT x를 십진수로 표현 
   printf("%3d\n", ~x); // -11

   return 0;
}
~10
-11

음수를 이진수로 표현하는 것은 수학의 2의 보수법을 활용합니다. C 언어는 알아서 이진수의 값을 양수 또는 정수로 출력해 줍니다.

7. 비트 연산자 4가지 모두 사용해보기

이번에는 비트 연산자 네가지를 모아서 사용해보겠습니다.

https://youtu.be/SM5RkmEe2-M

코드: bitwise_operator.c

// 비트 연산자: &, |, ~, ^
#include <stdio.h>

int main(void)
{
   int x = 0b1010; // 10진수: 10
   int y = 0b0110; // 10진수: 6

   int _and = x & y; // 0010 => 2
   printf("%d\n", _and); // 2

   int _or = x | y; // 1110 => 14
   printf("%d\n", _or); // 14

   int _xor = x ^ y; // 1100 => 12
   printf("%d\n", _xor); // 12

   // 2의 보수법에 의해서 1010+1 그리고 부호를 -로 -1011 => -11
   int _not = ~x; // ~~~~0101 => -11
   printf("%d\n", _not); // -11

   return 0;
}
2
14
12
-11

이진수 10100110에 대해서 &, |, ^ 연산자를 사용한 결과와 1010~연산자를 붙여 비트를 반전시키는 연산의 사용 결과가 나옵니다.

비트 연산의 기초와 C언어에서의 응용

비트 연산자는 컴퓨터 과학과 프로그래밍에서 필수적인 요소입니다. 이들은 데이터를 비트 수준에서 조작할 수 있게 해주며, 효율적인 코딩을 가능하게 합니다. 이 글에서는 C언어를 사용하여 비트 연산자인 AND, OR, XOR, 그리고 NOT의 사용 예를 살펴보겠습니다.

C언어의 비트 연산자를 활용한 소스 코드를 통해 각 연산자의 기능을 구현하였습니다.

코드: BitOperatorDescription.c

// 비트 연산자: AND(&), OR(|), XOR(^), NOT(~) 
#include <stdio.h>

int main(void)
{
    int x = 12; // 2진수로 1100
    int y = 10; // 2진수로 1010

    int a = x & y; // AND 연산: 1000 -> 8
    int o = x | y; // OR 연산: 1110 -> 14
    int e = x ^ y; // XOR 연산: 0110 -> 6
    // NOT 연산: 시스템에 따라 결과가 다를 수 있음. 32비트 int를 가정하면 큰 음수 값이 됨.
    int n = ~x;    // 11111111...11110011 -> -13

    printf("a: %d\n", a);
    printf("o: %d\n", o);
    printf("e: %d\n", e);
    printf("n: %d\n", n);

    getchar(); // 프로그램 종료 전 사용자의 입력을 기다립니다.

    return 0;
}

프로그램을 실행하면 다음과 같은 결과를 얻습니다:

  • AND 연산의 결과: 8
  • OR 연산의 결과: 14
  • XOR 연산의 결과: 6
  • NOT 연산의 결과: -13 (단, 이 값은 32비트 정수형을 사용할 때의 예상값입니다.)

위의 코드는 두 정수 xy에 대해 네 가지 비트 연산을 수행합니다. AND 연산자(&)는 두 비트 모두 1일 때만 1을 반환합니다. OR 연산자(|)는 두 비트 중 하나라도 1이면 1을 반환합니다. XOR 연산자(^)는 두 비트가 서로 다를 때 1을 반환합니다. NOT 연산자(~)는 모든 비트를 반전시킵니다. 여기서 주의해야 할 점은 NOT 연산의 결과는 시스템의 int 크기에 따라 달라질 수 있으며, 일반적으로 사용되는 32비트 시스템에서는 큰 음수 값을 반환하게 됩니다. 이러한 비트 연산자들은 암호화, 오류 검출, 데이터 압축 등 다양한 분야에서 활용됩니다.

8. 시프트 연산자

시프트 연산자(Shift Operator)는 정수 데이터가 담겨 있는 메모리의 비트를 왼쪽(<<) 또는 오른쪽(>>)으로 지정한 비트만큼 이동시켜주는 기능을 제공합니다. 시프트 연산자를 사용하면 비트의 자리를 다음과 같이 이동할 수 있습니다.

  • x << y : xy만큼 왼쪽으로 이동
  • x >> y : xy만큼 오른쪽으로 이동

예를 들어, 정수형 데이터인 2를 이진수로 표현하면 0010입니다. 왼쪽(<<) 시프트 연산자를 사용하여 한 칸 이동하면 0100이 됩니다. 이진수 0010을 오른쪽(>>) 시프트 연산자를 사용하여 한 칸 이동하면 0001이 됩니다.

비트 연산자의 사용 예를 표로 정리하면 다음과 같습니다.

연산자 사용 예 설명
<< 변수 << 비트값; 비트값 만큼 왼쪽으로 비트 이동
결괏값이 변수의 값 * 2의 비트값 제곱 == 비트당 2배
변수의 값 곱하기 2의 거듭제곱
>> 변수 >> 비트값; 비트값 만큼 오른쪽으로 비트 이동
결괏값이 변수의 값 / 2의 비트값 제곱 == 비트당 1/2배
변수의 값 나누기 2의 거듭제곱

시프트 연산자에 대한 내용을 그림으로 표현하면 다음과 같습니다.

그림: 시프트 연산자

시프트 연산자

정수 2의 이진수인 0010을 왼쪽으로 2칸 이동하면 1000이 되어 정수 8이 됩니다.

정수 40의 이진수인 0101000를 오른쪽으로 2칸 이동하면 0001010이 되어 정수 10이 됩니다.

시프트 연산자 사용하기

코드로 시프트 연산자를 사용해 보도록 하겠습니다.

코드: shift_operator.c

// shift_operator.c
#include <stdio.h>

int main(void)
{
   int num = 2; // 0010 

   int left = num << 1; // 0010 -> 0100: 4
   int right = num >> 1; // 0010 -> 0001: 1

   printf("%d\n", left); // 4
   printf("%d\n", right); // 1

   return 0;
}
4
1

이진수 0010을 왼쪽으로 비트를 한 칸 이동하면 0100이 되고 이진수 0010을 오른쪽으로 비트를 한 칸 이동하면 0001이 됩니다. 시프트 연산자는 내부적으로는 이진수로 계산이 되지만 정수형 데이터이기에 출력할 때에는 그대로 십진수로 표현됩니다.

C 언어를 이용한 비트 연산 기초

C 프로그래밍 언어는 낮은 수준의 데이터 조작을 가능하게 하는 강력한 기능을 제공하는데, 그중 비트 연산이라는 개념이 있습니다. 이번에는 C 언어의 비트 연산자를 사용하여 정수 값들을 어떻게 조작할 수 있는지 보여주는 간단한 예제를 살펴보겠습니다.

아래의 C 언어 소스 코드는 비트 연산자의 사용 예를 보여줍니다.

코드: ShiftOperatorDemo.c

#include <stdio.h>

int main(void)
{
    int number = 2; // 0010

    printf("%d\n", number << 1); // 0010 -> 0100 : 4
    printf("%d\n", number >> 1); // 0010 -> 0001 : 1

    return 0;
}

이 프로그램을 실행하면 다음과 같은 결과가 출력됩니다.

4
1

이 프로그램은 먼저 int 타입의 변수 number를 선언하고 2라는 값을 할당합니다. 이진 표현에서 2는 0010으로 나타낼 수 있습니다. number << 1number의 모든 비트를 왼쪽으로 한 자리 이동시키는데, 이것은 값에 2를 곱하는 것과 같은 결과를 가져옵니다(4). number >> 1number의 모든 비트를 오른쪽으로 한 자리 이동시키는데, 이것은 값이 2로 나누어지는 것과 같은 결과를 가져옵니다(1). 이러한 비트 연산자들은 프로그램의 효율성을 높이고, 성능이 중요한 애플리케이션에서 매우 유용합니다.

비트 및 시프트 연산자를 할당 연산자와 함께 사용하기

비트 연산자와 시프트 연산자는 할당 연산자와 결합하여 사용할 수 있으며, 이를 통해 코드의 간결성을 높일 수 있습니다. 다음 예시들은 비트 및 시프트 연산자가 할당 연산자와 어떻게 함께 사용되는지 보여줍니다.

  • A &= B; (A와 B의 AND 연산 후 결과를 A에 할당)
  • A |= B; (A와 B의 OR 연산 후 결과를 A에 할당)
  • A ^= B; (A와 B의 XOR 연산 후 결과를 A에 할당)
  • A <<= B; (A를 B만큼 왼쪽으로 시프트한 후 결과를 A에 할당)
  • A >>= B; (A를 B만큼 오른쪽으로 시프트한 후 결과를 A에 할당)

이러한 연산자들은 특히 성능에 민감한 저수준 프로그래밍에서 유용하게 사용됩니다. 아래의 예제 코드와 실행 결과를 통해 이 개념을 더 깊이 이해해 봅시다.

코드 예제: bitwise_assignment.c

// bitwise_assignment.c
#include <stdio.h>

int main(void)
{
    int num1 = 4; // 초기값: 0100 (2진수)
    int num2 = 4;
    int num3 = 4;
    int num4 = 4;
    int num5 = 4;

    num1 &= 5;  // 4(0100) AND 5(0101) => 결과: 4(0100)
    num2 |= 1;  // 4(0100) OR 1(0001) => 결과: 5(0101)
    num3 ^= 2;  // 4(0100) XOR 2(0010) => 결과: 6(0110)
    num4 <<= 1; // 4(0100) 왼쪽으로 1 시프트 => 결과: 8(1000)
    num5 >>= 1; // 4(0100) 오른쪽으로 1 시프트 => 결과: 2(0010)

    printf("%d\n", num1); // 출력: 4
    printf("%d\n", num2); // 출력: 5
    printf("%d\n", num3); // 출력: 6
    printf("%d\n", num4); // 출력: 8
    printf("%d\n", num5); // 출력: 2

    return 0;
}

실행 결과:

4
5
6
8
2

이 코드는 비트 연산과 시프트 연산의 결과를 변수에 할당하는 과정을 보여줍니다. C 언어 같은 저수준 프로그래밍 언어에서는 이런 연산이 특히 유용하지만, 일반적인 애플리케이션 개발에서는 자주 사용되지 않는 것이 사실입니다. 이 예제에서 다룬 기본 개념을 이해한 후 다음 주제로 넘어가셔도 충분합니다.

9. 기타 연산자와 연산자 우선순위 이해하기

이번 섹션에서는 조건 연산자와 나머지 연산자를 포함하여, 연산자의 우선순위에 대해 알아보겠습니다.

조건 연산자 (3항 연산자)

조건 연산자는 ? : 형태를 가지며, 조건에 따라 두 개의 다른 결과 중 하나를 반환할 때 사용됩니다. 조건 연산자는 세 개의 항을 가지기 때문에 3항 연산자라고도 불립니다. 이 연산자는 if-else 문의 축약형으로 볼 수 있습니다.

조건 연산자의 사용 예:

(5 > 3) ? "TRUE" : "FALSE";

이 경우, 조건은 참이므로 "TRUE"가 반환됩니다.

조건 연산자의 구조는 다음과 같습니다:

  • 조건식 ? 식1 : 식2;

여기서 조건식이 참이면 식1이, 거짓이면 식2가 실행됩니다. 결과적으로, 조건에 따라 값1 또는 값2 중 하나가 반환됩니다.

조건 연산자의 추가 설명

조건 연산자, 즉 ?:는 조건을 평가하고 그 결과에 따라 두 개의 가능한 식 중 하나를 선택하여 그 결과를 반환합니다. 예를 들어:

(tempInCelsius < 20.0) ? "Cold." : "Perfect!";

위의 예제에서 조건 연산자는 다음 구조를 따릅니다:

condition ? consequent : alternative

condition은 참(true) 또는 거짓(false)으로 평가되어야 하며, 참일 경우 consequent 식이, 거짓일 경우 alternative 식이 실행되어 그 결과가 반환됩니다. 조건 연산자는 오른쪽으로 결합됩니다. 예를 들어:

a ? b : c ? d : e;

이는 다음과 같이 해석됩니다:

a ? b : (c ? d : e);

기억 방법:

조건 연산자의 동작 방식을 기억하기 위한 간단한 방법은 다음과 같은 구문을 사용하는 것입니다:

조건이 참인가요? 참 : 거짓;

이로써, 조건 연산자와 그 사용법, 구조에 대해 자세히 알아보았습니다.

조건 연산자 활용하기

조건 연산자, 또는 3항 연산자를 활용하는 방법에 대해 알아보겠습니다. 이 연산자는 간단한 조건부 로직을 식 한 줄로 표현할 수 있게 해줍니다.

코드 예제: conditional_operator.c

// conditional_operator.c
#include <stdio.h>

int main(void)
{
    int num = 3;
    // num이 짝수면 "짝수", 아니면 "홀수"를 result에 저장
    const char* result = (num % 2 == 0) ? "짝수" : "홀수";

    printf("%d은(는) %s입니다.\n", num, result);

    return 0;
}

출력 결과:

3은(는) 홀수입니다.

위의 코드에서 num 변수에 저장된 값은 3입니다. (num % 2 == 0) 조건식은 num이 짝수인지 확인합니다. 이 경우, 3은 짝수가 아니므로 조건식은 거짓(false)이며, 따라서 "홀수"가 result 변수에 저장됩니다. 즉, 조건이 참일 경우 "짝수"를, 그렇지 않을 경우 "홀수"를 반환합니다. 이러한 방식으로, 조건 연산자는 if문의 간단한 대체 수단으로 사용될 수 있습니다.

조건 연산자로 최댓값 설정하기

조건 연산자를 사용하여 변수에 할당될 최댓값을 설정하는 예제를 살펴보겠습니다.

https://youtu.be/uySkLHY3wR8

코드 예제: operator_max_size.c

// operator_max_size.c
#include <stdio.h>

int main(void)
{
    const int max_size = 20; // 최대 크기를 20으로 설정
    int page_size = 0;

    page_size = 10;
    // 조건 연산자를 사용하여 page_size가 max_size보다 큰 경우 max_size를, 그렇지 않으면 page_size를 반환
    page_size = (page_size > max_size) ? max_size : page_size;
    printf("%d\n", page_size); // 10 출력

    page_size = 50;
    // 같은 로직으로, 이번엔 20이 출력됨
    page_size = (page_size > max_size) ? max_size : page_size;
    printf("%d\n", page_size); // 20 출력

    return 0;
}

출력 결과:

10
20

이 예제에서는 page_size 변수에 입력되는 값이 20을 초과하는 경우 항상 20으로 제한하는 로직을 구현했습니다.

문자 크기 비교를 위한 조건 연산자 활용

조건 연산자를 이용해 문자의 ASCII 값 기준 크기를 비교하는 방법을 살펴보겠습니다.

https://youtu.be/VtEgYfVEqdQ

코드 예제: ternary_operator.c

// ternary_operator.c
#include <stdio.h>

int main(void)
{
    const char* result1 =
        ('A' < 'B') ? "'A'는 'B'보다 작습니다." : "'A'와 'B'의 크기를 비교합니다.";
    const char* result2 =
        ('Z' < 'a') ? "'Z'는 'a'보다 작습니다." : "대문자와 소문자의 크기를 비교합니다.";

    printf("%s\n", result1); // 출력: 'A'는 'B'보다 작습니다.
    printf("%s\n", result2); // 출력: 'Z'는 'a'보다 작습니다.

    return 0;
}

출력 결과:

'A'는 'B'보다 작습니다.
'Z'는 'a'보다 작습니다.

이 예에서 문자들의 크기 비교는 ASCII 값에 기반하여 이루어집니다. ASCII 값은 A에서 Z로 갈수록 증가하며, a에서 z로 이어지면서 또한 증가합니다. 이 규칙에 따라 'A''B'보다 작고, 대문자 'Z'는 소문자 'a'보다 작다는 결과를 얻게 됩니다.

조건 연산자로 절댓값 구하기

3항 조건 연산자를 사용하여 정수의 절댓값을 구하는 방법을 알아봅시다.

코드 예제: absolute_value.c

// absolute_value.c
#include <stdio.h>

int main(void)
{
    int num = -21; // 음수 값
    int abs = (num < 0) ? -num : num; // 조건 연산자를 통한 음수 값의 부호 변환
    printf("%d의 절댓값: %d\n", num, abs);

    return 0;
}

출력 결과:

-21의 절댓값: 21

이 코드는 num 변수의 값이 음수인 경우 부호를 반전시켜 절댓값을 구합니다. 조건 연산자는 이처럼 간결하게 조건에 따른 값의 선택이 필요할 때 유용하게 사용됩니다.

삼항 연산자를 사용한 3의 배수 판별

이 프로그램은 삼항 연산자를 활용하여 사용자가 입력한 정수가 3의 배수인지 판별합니다. 3의 배수일 경우 "true"를, 그렇지 않을 경우 "false"를 출력합니다.

코드 파일: judge_ternary.c

// judge_ternary.c
// 사용자로부터 정수를 입력받아 3의 배수인지 판별하여 결과를 출력하는 프로그램
#define _CRT_SECURE_NO_WARNINGS // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>

int main(void)
{
    int num = 0;  // 사용자 입력을 저장할 변수
    printf("정수 입력: ");
    scanf("%d", &num);  // 사용자로부터 정수 입력 받기

    // 삼항 연산자를 사용하여 3의 배수 판별 후 결과 출력
    printf("%s\n", (num % 3 == 0) ? "true" : "false");

    return 0;
}

실행 예시

정수 입력: 3
true
정수 입력: 5
false

이 코드는 사용자의 입력을 받아 그 값이 3의 배수인지를 삼항 연산자를 통해 확인하고 결과를 즉각적으로 출력합니다.

콤마 연산자 활용하기

콤마 연산자는 여러 구문을 하나의 문장에서 순차적으로 실행할 수 있게 해주며, 변수 선언 시에도 사용됩니다. 이 연산자를 활용해 여러 변수를 한 줄에 선언하고 초기화할 수 있습니다.

예를 들어, 여러 변수를 동시에 선언하고 싶을 때는 다음과 같이 작성할 수 있습니다:

int a = 10, b = 20, c = 30;

이 코드에서 a, b, c는 모두 int 형 변수로 선언되며 각각 10, 20, 30으로 초기화됩니다.

IMPORTANT

콤마 연산자를 사용하면 코드의 가독성이 떨어질 수 있으므로, 복잡한 로직에는 사용을 자제하는 것이 좋습니다.

sizeof 연산자를 통한 데이터 타입 크기 확인

sizeof 연산자는 변수나 데이터 타입의 메모리 크기를 바이트 단위로 반환합니다. 이 연산자는 플랫폼에 따라 반환되는 크기가 다를 수 있으므로, 특정 플랫폼의 메모리 할당 크기를 확인할 때 유용합니다.

코드 예제: sizeof_operator.c

// sizeof_operator.c
#include <stdio.h>

int main(void)
{
    printf("sizeof(데이터 타입)\n");
    printf("  char 타입: %llu byte\n", sizeof(char));
    printf("   int 타입: %llu byte\n", sizeof(int));
    printf("  long 타입: %llu byte\n", sizeof(long));
    printf(" float 타입: %llu byte\n", sizeof(float));
    printf("double 타입: %llu byte\n", sizeof(double));

    return 0;
}

출력 결과:

sizeof(데이터 타입)
  char 타입: 1 byte
   int 타입: 4 byte
  long 타입: 4 or 8 byte (플랫폼에 따라 다름)
 float 타입: 4 byte
double 타입: 8 byte

sizeof 연산자는 괄호를 사용해 특정 데이터 타입이나 변수의 크기를 바이트 단위로 측정합니다. 예를 들어, double 타입의 크기를 알고 싶다면 sizeof(double)과 같이 사용합니다. 이는 C 언어 프로그래밍을 할 때 데이터 타입이 차지하는 메모리 크기를 정확히 알고 싶을 때 매우 유용합니다.

NOTE

sizeof, unsigned int, size_t

sizeof 연산자의 결과는 unsigned int입니다. unsigned int 타입은 C 언어에서는 size_t 타입으로 대체해서 사용해도 됩니다. size_t 타입은 뒤에서 자세히 다루겠습니다.

연산자 우선순위 이해하기

프로그래밍에서 연산자 우선순위는 여러 연산자가 동시에 나타날 때, 어떤 연산자가 먼저 계산될지를 결정합니다. 예를 들어, 산술 연산자 중 *+보다 우선순위가 높으므로 먼저 계산됩니다. 우선순위를 명시적으로 변경하고 싶다면, 괄호 연산자 ()를 사용할 수 있습니다.

표: 우선순위가 적용된 산술 연산 구문

우선순위가 적용된 산술 연산 구문

다음은 C 언어에서 주요 연산자의 우선순위입니다.

표: C 언어에서의 주요 연산자 우선순위

연산자 우선순위

IMPORTANT

연산자 우선순위를 일일이 외울 필요는 없으며, 괄호 ()를 적절히 사용하여 우선순위를 명확하게 지정하는 습관을 기르는 것이 좋습니다. 이렇게 하면 코드의 가독성과 정확성이 향상됩니다.

연산자 우선순위를 명확히 이해하기 위해, 다음과 같은 산술 연산 예를 살펴봅시다.

코드: operator_precedence.c

// operator_precedence.c
// C 언어의 연산자 우선 순위: 괄호(그룹) > 곱셈/나눗셈 > 덧셈/뺄셈 > 왼쪽에서 오른쪽
#include <stdio.h>

int main(void)
{
   printf("%d\n", 3 + 4 * 2); // 곱하기가 우선: 3 * 8 => 11
   printf("%d\n", (3 + 4) * 2); // 괄호가 우선: 7 * 2 => 14
   printf("%d\n", 10 / 5 * 2 + 1); // 왼쪽에서 오른쪽으로: 2 * 2 + 1 => 5
   printf("%d\n", 15 / (5 * (2 + 1))); // 안쪽 괄호가 우선: 15 / (5 * 3) => 1
   return 0;
}
11
14
5
1

연산자 우선순위를 이해하고 수식 계산하기

연산자 우선순우에 대한 추가 예제는 다음 링크를 참고하세요.

연산자 우선순위를 이해하고 수식 계산하기

장 요약

조건 연산자(또는 3항 연산자)는 조건에 따라 다른 값을 반환하고자 할 때 유용하게 사용됩니다. 이 연산자를 통해 간결하게 조건부 로직을 구현할 수 있으며, if 문으로도 대체할 수 있습니다. 본 장에서는 콤마 연산자와 연산자 우선순위에 대해서도 학습하였습니다. 이러한 기본적인 연산자들의 이해는 프로그래밍에서 로직을 구성하는 데 필수적입니다. 이제 제어문에 대해 더 자세히 배워보도록 하겠습니다.

정보처리기능사 실기 문제: 조건 연산자를 사용한 변수 값 계산

다음은 C 언어를 사용한 프로그래밍 문제입니다. 이 문제에서는 조건 연산자(? :)를 사용하여 주어진 조건에 따라 변수의 값을 결정하고, 그 결과를 출력하는 프로그램을 작성하는 것을 목표로 합니다.

문제: 주어진 C 프로그램 코드에서 조건 연산자를 사용하여 변수 result의 값을 결정하고, 최종적으로 result, b, c의 값을 출력합니다. 사용자는 조건 연산자의 작동 원리를 이해하고, 주어진 조건에 따라 어떻게 변수들의 값이 변경되는지 예측해야 합니다.

#include <stdio.h>

main()
{
    int result, a = 100, b = 200, c = 300;
    // 조건 연산자를 사용한 값의 결정
    result = a < b ? b++ : --c;
    // 결과 출력
    printf("%d, %d, %d\n", result, b, c);
}

해결 방법:

  1. a, b, c 세 변수를 각각 100, 200, 300으로 초기화합니다.
  2. 조건 연산자를 사용하여 a < b의 조건을 평가합니다. 이 조건이 참이면 b++가 실행되어 resultb의 현재 값이 할당되고, b는 1 증가합니다. 거짓이면 --c가 실행되어 c는 1 감소한 값이 되고, result에는 감소된 c의 값이 할당됩니다.
  3. printf 함수를 사용하여 result, b, c의 최종 값을 출력합니다.

이 문제는 조건 연산자의 사용법과 변수 값의 동적 변경을 이해하는 데 중점을 둡니다. 조건 연산자는 간결한 조건식 내에서 값을 결정할 때 유용하게 사용됩니다.

정보처리산업기사 실기 시험 기출 문제

문제

다음 C 언어 프로그램이 실행되었을 때의 동작을 설명하고, 출력 결과를 예측하시오.

소스 코드 파일명: swap_xor_operation.c

#include <stdio.h>

main() {
    int n1 = 15, n2 = 22;
    n1 ^= n2;
    n2 ^= n1;
    n1 ^= n2;
    printf("%d %d", n1, n2);
}

입력 예시

이 프로그램은 입력을 받지 않습니다.

출력 예시

22 15

해설

이 프로그램은 두 개의 변수 n1n2의 값을 교환한 후 그 결과를 출력합니다. 교환 과정은 XOR 연산자 ^=를 사용하여 이루어집니다.

  1. #include <stdio.h>는 표준 입출력 함수를 사용하기 위한 표준 라이브러리를 포함합니다.
  2. main() 함수는 프로그램의 시작점입니다.
  3. int n1 = 15, n2 = 22;는 두 개의 정수 변수 n1n2를 선언하고 각각 15와 22로 초기화합니다.
  4. n1 ^= n2;n1n2의 값을 XOR 연산하여 n1에 저장합니다. 이 연산 후 n1은 두 값의 XOR 결과를 가집니다.
  5. n2 ^= n1;는 변경된 n1과 원래의 n2를 XOR 연산하여 n2에 저장합니다. 이 연산은 원래 n1의 값을 n2에 저장하는 효과를 낳습니다.
  6. n1 ^= n2;는 변경된 n2와 XOR 연산된 n1을 다시 XOR 연산하여 n1에 저장합니다. 이 연산은 원래 n2의 값을 n1에 저장하는 효과를 낳습니다.
  7. printf("%d %d", n1, n2);는 변경된 n1n2의 값을 출력합니다. 따라서, 22 15가 출력됩니다.

이 프로그램은 초기에 n1n2에 각각 15와 22를 할당한 후, XOR 연산을 통해 이들의 값을 교환하여 최종적으로 n1에는 22, n2에는 15가 저장되므로 22 15가 출력됩니다.

정보처리산업기사 실기 시험 기출 문제 - 비트 및 논리 연산 출력

문제

다음 C 프로그램이 실행되었을 때의 동작을 설명하고, 출력 결과를 예측하시오.

소스 코드 파일명: bitwise_logical_operations.c

#include <stdio.h>

main() {
    int x = 1;
    printf("%d\n", !(x > 0));
    printf("%d\n", (x > 0 || x < 4));
    printf("%d\n", x << 2);
    printf("%d\n", x & 2);
    printf("%d\n", x % 3);
}

입력 예시

이 프로그램은 입력을 받지 않습니다.

출력 예시

0
1
4
0
1

해설

이 프로그램은 변수 x의 값에 대한 여러 가지 비트 연산 및 논리 연산을 수행하고 그 결과를 출력합니다.

  1. int x = 1;는 정수 변수 x를 선언하고 1로 초기화합니다.
  2. printf("%d\n", !(x > 0));x > 0의 조건이 참이므로, ! 연산자를 사용하여 그 결과를 반전시킵니다. C 언어에서 논리적 '참'은 1, '거짓'은 0으로 표현됩니다. 따라서 0이 출력됩니다.
  3. printf("%d\n", (x > 0 || x < 4));x > 0 또는 x < 4의 조건 중 하나라도 참이면 1을 반환합니다. 두 조건 모두 참이므로, 1이 출력됩니다.
  4. printf("%d\n", x << 2);x의 값을 왼쪽으로 2비트 이동시킵니다. x의 값인 1을 2진수로 01로 표현할 때, 왼쪽으로 2비트 이동시키면 100이 되어, 10진수로 4가 됩니다. 따라서 4가 출력됩니다.
  5. printf("%d\n", x & 2);x의 값과 2를 비트 AND 연산합니다. x의 2진수 값 01과 2의 2진수 값 10을 AND 연산하면 00이 되어, 10진수로 0이 됩니다. 따라서 0이 출력됩니다.
  6. printf("%d\n", x % 3);x를 3으로 나눈 나머지를 출력합니다. 1을 3으로 나눈 나머지는 1이므로, 1이 출력됩니다.

이 프로그램은 기본적인 비트 연산과 논리 연산을 통해 다양한 결과를 출력하며, 조건 연산자와 비트 이동 연산자 등의 사용 방법을 이해하는 데 도움을 줍니다. C 언어에서는 논리 연산 결과를 정수 1 (참)과 0 (거짓)으로 출력합니다.

정보처리산업기사 실기 시험 기출 문제 - 복합 비트 연산 및 조건식

문제

다음 C 프로그램이 실행되었을 때의 동작을 설명하고, 출력 결과를 예측하시오.

소스 코드 파일명: complex_bitwise_logical_operations.c

#include <stdio.h>

int main() {
    int a = 5, b = 9, c;
    c = b % 5 < 5 ? 1 : 0;
    c = c | (c << 3);
    c = (a < 5 || c >= 10) ? (c - a) : (c + a);
    printf("%d", c);
    return 0;
}

입력 예시

이 프로그램은 입력을 받지 않습니다.

출력 예시

14

해설

이 프로그램은 변수 a, b, c에 대한 복합 비트 연산 및 조건식을 이용하여 최종적으로 c의 값을 결정하고 출력합니다.

  1. int a = 5, b = 9, c;는 정수 변수 a, b, c를 선언하고 초기화합니다.
  2. c는 먼저 b % 5 < 5 조건을 평가하여 1로 설정됩니다.
  3. 그 다음, c는 자기 자신과 자기 자신을 3비트 왼쪽으로 이동한 값과의 비트 OR 연산 결과로 업데이트됩니다. c | (c << 3) 계산 결과 c9가 됩니다.
  4. 마지막으로, (a < 5 || c >= 10) 조건을 평가한 후, 해당 조건이 거짓이므로 (c + a) 연산을 수행하여 c14가 됩니다.
  5. 최종적으로, c의 값 14가 출력됩니다.

이 프로그램은 복합 비트 연산과 조건식을 사용하여 변수의 값을 조작하는 과정을 보여주며, 이러한 연산들이 어떻게 결합되어 최종 결과를 도출하는지를 이해하는 데 도움을 줍니다. C 언어를 사용하여 비트 연산과 조건 연산자를 활용하는 방법을 보여줍니다.

정보처리기사 실기 시험 기출 문제 - 조건 연산자 활용

문제

다음 C 프로그램이 실행되었을 때의 동작을 설명하고, 출력 결과를 예측하시오.

소스 코드 파일명: conditional_operator_usage.c

#include <stdio.h>

main() {
    int result, a = 100, b = 200, c = 300;
    result = a < b ? b++ : --c;
    printf("%d, %d, %d\n", result, b, c);
}

입력 예시

이 프로그램은 사용자로부터 입력을 받지 않습니다.

출력 예시

200, 201, 300

해설

이 프로그램은 조건 연산자(?:)를 사용하여 변수 a, b, c 간의 조건에 따라 연산을 수행하고 그 결과를 출력합니다.

  1. 초기에 변수 a, b, c는 각각 100, 200, 300으로 초기화됩니다.
  2. 조건 연산자를 사용한 result = a < b ? b++ : --c;ab보다 작으므로 참이 되어 b++ 연산을 수행하고, b의 원래 값(200)을 result에 할당한 후 b의 값을 1 증가시킵니다.
  3. printf("%d, %d, %d\n", result, b, c);는 각 변수의 최종 값을 출력합니다. 따라서 출력 결과는 result가 200, b가 201 (증가된 후의 값), c가 원래 값인 300이 됩니다.

이 프로그램은 조건 연산자를 활용하여 간단한 조건에 따른 값의 선택과 변수의 값을 조정하는 방법을 보여줍니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com