[태그:] <span>비트 논리곱</span>

컴퓨터 프로그램에는 관할 데이터 외에도 동작해야 할 논리를 표현하는 문법이 필요합니다.  C#에서는 프로그램 논리를 표현하기 위해 연산자와 식, 문 등을 제공합니다. 다른 언어를 접한 적이 있다면 속독으로 보셔도 되는 부분입니다.

2.2.1 연산자

연산자는 연산 기호와 피연산자를 사용하였을 때 수행할 행위와 결과 형식에 대한 약속입니다. 다음은 C#에서 제공되는 연산자들과 간략한 사용 예입니다.

▶ + (단항 연산자)

모든 숫자 형식에 제공이 됩니다. 연산 결과는 단순히 피연산자의 값으로 아무 의미가 없습니다.

static void Main(string[] args)
{
    int a = 3;
    Console.WriteLine("a:{0} +a:{1}", a, +a);
}

▶ + (이항 연산자)

숫자 형식 사이에 오면 두 수의 합을 계산합니다.

static void Main(string[] args)
{
    int i = 2;
    int j = 3;
    Console.WriteLine("{0}+{1}={2}", i, j, i+j);
}

문자열 사이에 오면 두 개의 문자열의 문자 컬렉션을 합한 문자열을 반환합니다.

static void Main(string[] args)
{
    string s1 = "hello";
    string s2 = "yahoo";
    Console.WriteLine("{0}+{1}={2}", s1, s2, s1+s2);
}

대리자 사이에 오면 두 개의 대리자에 위임된 행위를 위임한 대리자를 반환합니다. 이 부분은 9장 대리자와 이벤트에서 자세히 다루겠습니다.

delegate void ExamDele();
class Program
{
    static void Main(string[] args)
    {
        ExamDele dele1 = new ExamDele(FunA);
        ExamDele dele2 = new ExamDele(FunB);
        ExamDele dele3 = dele1 + dele2;
        dele3();
    }
    static void FunA()
    {
        Console.WriteLine("FunA ");
    }
    static void FunB()
    {
        Console.WriteLine("FunB ");
    }
}

▶ – (단항 연산자)

모든 숫자 형식에 제공이 됩니다. 연산 결과는 단순히 피연산자의 -1을 곱한 값입니다.

static void Main(string[] args)
{
    int a = 3;
    Console.WriteLine("a:{0} -a:{1}", a, -a);
}

▶ – (이항 연산자)

숫자 형식 사이에 오면 두 수의 차를 계산합니다.

static void Main(string[] args)
{
    int i = 2;
    int j = 3;
    Console.WriteLine("{0}-{1}={2}", i, j, i-j);
}

열거형 형식 사이에 오면 두 열거형 값의 차를 계산합니다. 열거형은 4장 값 형식에서 자세히 다루겠습니다.

enum DemoEnum{    A=3, B=8    }
static void Main(string[] args)
{
    DemoEnum e1 = DemoEnum.A;
    DemoEnum e2 = DemoEnum.B;
    Console.WriteLine("{0} - {1} = {2}", e1, e2, e1 - e2);
}

▶ * (단항 연산자)

안전하지 않은 컨텍스트에서만 사용이 가능합니다. 포인터 변수 선언문에 사용하거나 피연산자로 포인터 형식이 오면 간접연산이 수행됩니다. 안전하지 않은 컨텍스트와 포인터에 대한 사항은 MSDN을 참고하세요. 참고로 C#에서 안전하지 않은 코드를 사용하기 위해서는 [프로젝트]=>[속성]=>[빌드 탭]을 선택한 후에 안전하지 않은 코드 허용 체크 박스를 선택하여 빌드해야 합니다.

static void Main(string[] args)
{
    unsafe
    {
        int a=3;
        int* p = &a;
        *p = 4;
        Console.WriteLine(a.ToString());
    }
}

▶ * (이항 연산자)

숫자 형식 사이에 오면 두 수의 곱을 계산합니다.

static void Main(string[] args)
{
    int i = 2;
    int j = 3;
    Console.WriteLine("{0}*{1}={2}", i, j, i * j);
}

▶ / (이항 연산자)

숫자 형식 사이에 오면 두 수의 나눗셈을 계산합니다. 두 개의 피연산자가 정수형일 때에는 우항에 0이 오면 DivideZeroException이 발생합니다. 두 개의 피연산자가 정수형일 때에는 내림 법칙에 의해 계산됩니다.

피연산자 중의 하나라도 실수가 오면 연산 결과는 실수가 됩니다. 피연사자 중의 하나라도 실수일 때 우항에 0이 오면 연산 결과는 Infinity가 됩니다. 피연산자 중의 하나라도 실수일 때 좌우항이 모두 0이면 연산 결과는 NaN이 됩니다.

static void Main(string[] args)
{
    int i = 3;
    int j = 2;
    int intzero = 0;
    Console.WriteLine("{0}/{1}={2}", i, j, i / j); //결과: 1 ,내림 법칙 적용됨
    //Console.WriteLine("{0}/{1}={2}", i, intzero, i / intzero); DivideZeroException 발생

    float floatzero = 0.0f;
    Console.WriteLine("{0}/{1}={2}", i, floatzero, i / floatzero); //결과: Infinity
    Console.WriteLine("{0}/{1}={2}", intzero, floatzero, intzero / floatzero); //결과: Nan
}

▶ 결과

3/2=1
3/0=Infinity
0/0=NaN

▶ % (이항 연산자)

숫자 형식 사이에 오면 두 수를 나누었을 때 나머지를 계산합니다.

static void Main(string[] args)
{
    Console.WriteLine(3 % 2);
    Console.WriteLine(-4 % 10 );
    Console.WriteLine(6.2 % 10);
    Console.WriteLine(-5.2 % 2.1);
}

▶결과

1
-4
6.2
-1

▶ & (단항 연산자)

안전하지 않은 컨텍스트에서만 사용이 가능하며 피연산자의 주소를 계산하여 반환합니다.

static void Main(string[] args)
{
    unsafe
    {
        int i=2;
        int* p = &i;
        Console.WriteLine("{0}", *p);
    }
}

▶ & (이항 연산자)

정수 형식 사이에 오면 비트논리곱을 계산합니다. bool 형식 사이에 오면 논리곱을 계산합니다. 예를 들어 6&3은 0110(2진수)&0011(2진수)이므로 피연산자의 같은 자리의 비트가 1인 곳만 1이므로 0010(2진수)가 되어 연산 결과는 2가 됩니다.

static void Main(string[] args)
{
    Console.WriteLine("{0}&{1}={2}", 6, 3, 6 & 3);
    Console.WriteLine("{0}&{1}={2}", true, false, true & false);
}

▶결과

6&3=2
True&False=False

▶ | (이항 연산자)

정수 형식 사이에 오면 비트논리합을 계산합니다. bool 형식 사이에 오면 논리합을 계산합니다. 예를 들어 6 | 3은 0110(2진수) | 0011(2진수)이므로 피연산자 중 어느 하나라도 1인 자리는 1이 되므로 0111(2진수)가 되어 연산  결과는 7이 됩니다.

static void Main(string[] args)
{
    Console.WriteLine("{0}|{1}={2}", 6, 3, 6 | 3);
    Console.WriteLine("{0}|{1}={2}", true, false, true | false);
}

▶결과

6|3=7
True|False=True

▶ ^ (이항 연산자)

정수 형식 사이에 오면 비트 논리 배타합(xor) 연산을 계산합니다. bool 형식 사이에 오면 논리 배타합 연산을 계산합니다. 배타합이란 서로 다를 때 참이고 같을 때 거짓을 말합니다. 예를 들어 6 ^ 3은 0110(2진수) ^ 0011(2진수)이므로 피연산자의 같은 자리의 비트가 서로 다르면 1이 되므로 0101(2진수)가 되어 연산 결과는 5입니다.

static void Main(string[] args)
{
    Console.WriteLine("{0}^{1}={2}", 6, 3, 6 ^ 3);
    Console.WriteLine("{0}^{1}={2}", true, false, true ^ false);
}

▶결과

6^3=5
True^False=True

▶ << (이항 연산자)

왼쪽 피연산자를 둘째 피연산자(int) 만큼 왼쪽으로 비트 이동합니다. 왼쪽 피연산자의 자료형이 32비트 정수일 때는 오른쪽 피연산자의 하위 5비트만 유효합니다. 64비트 정수일 때는 오른쪽 피연산자의 하위 6비트만 유효합니다. 그리고 비트 이동으로 빈 자리는 0으로 채워집니다.

예를 들어 int형 변수 i의 값이 5일 때 i<<2를 요청하면 2비트 이동하여 연산 결과가 20이 됩니다.

그리고 int형 변수 i의 값이 5일 때 i<<33을 요청하면 33이 0100001(2진수)이므로 하위 5비트인 00001(2진수)만큼 비트 이동하여 연산 결과가 a(16진수)가 됩니다.

만약, long형 변수 l의 값이 5일 때 l<<33을 요청하면 0100001(2진수)이므로 하위 6비트인 100001(2진수)만큼 비트 이동하여 연산 결과가 a00000000(16진수)가 됩니다.

그리고 long형 변수 l의 값이 5일 때 l<<65를 요청하면 01000001(2진수)이므로 하위 6비트인 000001(2진수)만큼 비트 이동하여 연산 결과가 a(16진수)가 됩니다.

static void Main(string[] args)
{
    int i = 5;
    long l = 5;
    Console.WriteLine("int: {0}<<{1}={2}", i, 2, i << 2);
    Console.WriteLine("int: {0}<<{1}={2:X}", i, 33, i << 33);
    Console.WriteLine("long: {0}<<{1}={2:X}", l, 33, l << 33);
    Console.WriteLine("long: {0}<<{1}={2:X}", l, 65, l << 65);
}

▶결과

int: 5<<2=20
int: 5<<33=a
long: 5<<33=a00000000
long: 5<<65=a

▶ >> (이항 연산자)

왼쪽 피연산자가 부호 있는 정수(int, long) 형식이면 빈 자리는 부호 비트로 채워지고 부호 없는 정수(uint, ulong) 형식이면 빈자리는 0으로 채워집니다.

static void Main(string[] args)
{
    int i = -5;
    uint u = 5;
    Console.WriteLine("{0}>>{1}={2}", i, 2, i >> 2);
    Console.WriteLine("{0}>>{1}={2}", u, 2, u >> 2);
}

▶결과

-5>>2=-2
5>>2=1

▶ ~ (단항 연산자)

피연산자를 비트 보수 연산을 합니다. 피연사자는 int, uint, long, ulong 형식이 올 수 있습니다.

static void Main(string[] args)
{
    int i = -9;
    uint u = 9;
    Console.WriteLine("~{0:X8} => {1:X8}", i, ~i);
    Console.WriteLine("~{0:X8} => {1:X8}", u, ~u);
}

▶결과

~FFFFFFF7 => 00000008
~00000009 => FFFFFFF6

▶ = (이항 연산자)

피연산자는 같은 형식이거나 오른쪽 피연산자를 암시적으로 왼쪽 피연산자 형식으로 변환 가능해야 합니다.

static void Main(string[] args)
{
    int i = 3;
    int re = 0;
    re = i;
    Console.WriteLine("re:{0}", re);
}

▶ +=, -=, *=, /=,%=,&=,|=,^=,<<=,>>= (이항 연산자)

왼쪽 피연산자와 오른쪽 피연산자를 더하기(빼기, 곱하기,…)연산 결과를 왼쪽 피연산자에 대입합니다.

static void Main(string[] args)
{
    int i = 3;
    int re = 0;
    re += i;
    Console.WriteLine("re+={0} 수행 후  re:{1}", i,re);
    re -= i;
    Console.WriteLine("re-={0} 수행 후 re:{1}", i, re);
}

▶ ++, — (단항 연산자)

자기 자신을 1 증가시키거나 1 감소시킵니다. 연산자가 앞에 오면 연산 결과는 연산을 수행한 후의 자기 자신입니다. 연산자가 뒤에 오면 연산 결과는 연산을 수행하기 전의 값입니다.

static void Main(string[] args)
{
    int i = 3;
    int re;
    Console.WriteLine("현재 i:{0}", i);
    re = ++i;
    Console.WriteLine("++i, 연산결과 {0}, i:{1} ", re,i);
    re = i++;
    Console.WriteLine("++i, 연산결과 {0}, i:{1} ", re, i);
}

▶ ==,  != (단항 연산자)

두 개의 피연산자가 같은지 다른지를 판별하여 bool 형식 결과 값을 반환합니다. 값 형식과 string 형식이 피연산자로 올 경우에는 값이 같은지를 판별합니다. string 형식을 제외한 참조 형식이 피연산자로 올 경우에는 참조하는 개체가 같은지를 판별합니다.

static void Main(string[] args)
{
    int i = 3;
    int j = 3;
    Console.WriteLine("i:{0} j:{1}, == 결과:{2}", i, j, i == j);
    object o1 = 3;
    object o2 = 3;
    Console.WriteLine("o1:{0} o2:{1}, == 결과:{2}", o1, o2, o1 == o2);
}

▶ 결과

i:3 j:3, == 결과:True
o1:3 o2:3, == 결과:False

▶ <, <=, >, >= (이항 연산자)

피연산자의 크기를 비교한 결과를 bool 형식 값으로 반환합니다. 모든 숫자 형식과 열거형이 피 연산자로 올 수 있습니다.

static void Main(string[] args)
{
    int i = 2;
    int j = 3;
    Console.WriteLine("{0}<{1} => {2}", i, j, i < j);
}

▶ &&, || (이항 연산자)

피연산자로 bool 형식이 올 수 있으며 논리곱과 논리합을 수행합니다. && 연산은 왼쪽 피연산자가 거짓인 때 뒤에 코드를 수행하지 않습니다. || 연산은 왼쪽 피연산자 참이면 뒤에 코드를 수행하지 않습니다.

static void Main(string[] args)
{
    bool re = TestA() && TestB();
    Console.WriteLine("TestA() && TestB() 결과:{0}", re);

    re = TestB() || TestA();
    Console.WriteLine("TestB() || TestA() 결과:{0}", re);
}
private static bool TestB()
{
    Console.WriteLine("TestB");
    return true;
}
private static bool TestA()
{
    Console.WriteLine("TestA");
    return false;
}

▶ 결과

TestA
TestA()&&TestB() 결과:False
TestB
TestB() || TestA() 결과:True

▶ ! (단항 연산자)

bool 형식이 피연산자로 올 수 있으며 부정을 반환합니다.

static void Main(string[] args)
{
    bool flag = true;
    Console.WriteLine("!{0}=>{1}", flag, !flag);
}

▶ [] (이항 연산자)

배열의 요소에 접근할 때 사용할 수 있습니다. 또한, 요소 개체를 보관하는 컬렉션에서 요소를 접근하는 인덱서로 사용이 됩니다. 인덱서는 5장 캡슐화에서 다루겠습니다. 또한, 단항 연산자로 특성(Attribute)에 사용이 됩니다. 이 책에서는 특성은 다루지 않습니다.

static void Main(string[] args)
{
    int[] arr = new int[10];
    for (int i = 0; i < arr.Length; i++)
    {
        arr[i] = i + 1;
    }
    foreach (int i in arr)
    {
        Console.WriteLine(i.ToString());
    }
}

이 외에도 C#에서는 개체를 생성할 때 사용하는 new 연산자를 비롯하여 삼항 조건 연산자인 ?:와 왼쪽 피연산자가 null이 아닐 때 연산 결과로 왼쪽 피연산자를 반환하고 null일 때 오른쪽 피연산자를 반환하는 ??를 비롯하여 멤버에 접근하기 위한 .연산자 등을 제공합니다. 여기에 소개되지 않은 더 많은 연산자는 MSDN을 참고하시기 바랍니다.

Escort C#