[C# 13과 .NET 9] C# 연산자와 표현식 - 7
C#에서 표현식을 나타내는 데는 연산자가 사용될 수 있으며 여기서 연산자는 사칙연산자나 혹은 할당 연산자가 될 수 있습니다. 이 외에도 선행/후행연산자, 논리연산자, 삼항연산자등이 있는데 구체적으로 이들을 어떻게 구현할 수 있는지에 대해서도 알아볼 것입니다.
이전에 우리는 C#에서 변수와 상수의 구현방법에 대해 알아본바 있으며 다양한 변수와 상수의 유형에 대해서도 살펴보았습니다. 그러나 단순히 변수와 상수를 선언하는 것 이외에 C# Code에서 이들 변수를 어떻게 활용할 수 있는지에 대해 알아보는 것도 중요합니다. 이때 변수와 상수에 저장된 Data를 활용하는 주된 방법에 대해 논의해 볼 수 있으데 변수와 상수는 그 방법 중 하나인 표현식의 형태로서 다뤄볼 수 있습니다.
1. 표현식
대부분의 기본적인 표현식은 연산자와 그 대상이 되는 피연산자 그리고 할당연산자로 이루어져 있습니다. 아래 예제에서는 기본적인 표현식을 나타내고 있습니다.
int i = 1 + 2;
상기 예제에서 +연산자는 2개의 피연산자(1과 2)를 서로 결합하도록 하고 있으며 할당연산자(=)를 통해 i이름의 int형 변수로 그 결과를 할당하고 있습니다. 피연산자는 예제에서처럼 고정된 값뿐만 아니라 변수나 상수자체(혹은 둘다)가 되는 것도 가능한데 추후에는 C#에서 가능한 다양한 연산자를 직접 사용해 볼 것입니다.
2. 기본 할당 연산자
우리는 이미 몇차례에 걸쳐서 가장 기본적인 할당 연산자인 =연산자를 사용해 보았습니다. 이 할당연산자는 표현식의 결과를 변수로 할당하는 역할을 수행합니다. 따라서 해당 연산자는 2개 이상의 피연산자를 필요로 하며 이때 오른쪽 피 연산자가 할당되는 값이 됩니다. 특히 오른쪽 피연산자는 대부분 산술이나 논리적 평가를 수행하는 표현식에 해당하는데 아래 예제를 통해 유효한 할당연산자의 사용 방법을 확인할 수 있습니다.
int i1;
int i2 = 10;
int i3 = 20;
i1 = 30;
i1 = i2 + i3;
i1 = i2;
또한 할당연산자는 서로 연결할 수 있으며 이를 통해 다수의 변수에 같은 값을 할당할 수 있습니다. 이러한 방법은 아래 예제를 통해 확인할 수 있습니다.
int a, b, c;
a = b = c = 15;
3. 산술연산자
C#에서는 산술표현식을 위한 다양한 연산자를 제공하고 있습니다. 이들 연산자는 대부분 이항연산자에 속하며 2개의 피연산자를 필요로 합니다. 다만 단항 음수 연산자는 예외인데 이는 값이 양수가 아닌 음수임을 나타내는 데 사용될 수도 있기 때문입니다. 반면 뺄셈연산자로 사용되는 경우 2개의 피연산자(하나의 값은 다른 값을 통해 감산됨)를 필요로 합니다. 예를 들어
int x = 10;
int y = -5;
int r = x - y;
위 예제의 경우 변수 y는 -5값을 할당하고 있으며 마지막에 y에서 x의 값을 감산하게 되므로 변수 r의 결과는 -15가 됩니다. 아래 표에서는 C#에서 주로 사용되는 산술연산자를 확인할 수 있습니다.
-(음수) | 변수나 표현식의 값을 음수화합니다. |
* | 곱셈 연산자 |
/ | 나눗셈 연산자 |
+ | 덧셈 연산자 |
- | 뺄셈 연산자 |
% | 나머지 연산자 |
특히 곱셈 연산자는 단일 표현식에서도 사용됩니다. 예를 들어 아래 예제는 완벽하게 유효합니다.
int x = 10;
int y = 2;
int r = x * 5 + y - 2 / 4;
또한 C#은 표현식을 왼쪽에서 오른쪽으로 혹은 오른쪽에서 왼쪽순서로 평가하지 않고 수학적 원칙에 따라 표현식에 사용된 다양한 연산자의 우선순위로 연산의 순서가 지정됩니다. 연산자의 우선순위는 계산의 결과에 영향을 주기 때문에 이를 이해하는 것이 매우 중요합니다.
4. 연산자 우선순위
C#은 기초수학의 개념에 따라 연산자의 순서를 동일하게 적용합니다. 예를 들어 아래 표현식의 정답은
int r = 10 + 20 * 5;
150이 아닌 110이 됩니다. 연산자의 우선순위가 적용되기 때문에 *먼저이고 그다음 +순으로 계산이 이루어지며 이러한 규칙은 C#에서도 다르지 않기 때문입니다.
만약 이러한 기본적인 우선순위를 바꾸고자 한다면 표현식에서 우선적으로 연산되어야 하는 부분을 괄호로 감싸주면 됩니다. 이러한 규칙 역시 기초 수학에 포함되는 개념이며, 따라서 상기 표현식을 왼쪽에서 오른쪽순서로 연산하도록 하여 값이 150이 되게 하려면 다음과 같이 처리해 줄 수 있습니다.
int r = (10 + 20) * 5;
표현식에서 괄호가 사용되면 해당 부분을 가장먼저 우선적으로 평가하게 되므로 값은 150이 될 수 있습니다.
아래 표는 가장 우선순위가 높은 연산자의 순서를 위에서 아래의 순서로 정리한 것입니다.
- +, -, !, ~, ++x, -x, (T)x
- * / %
- + -
- << >>
- < > <= >= is as
- == !=
- &
- ^
- |
- &&
- ||
- :?
- =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
특정 표현식이 완전하게 평가되고 나면 해당 표현식의 결과가 할당되는 순서이므로 위의 표에서 나타낸 것처럼 할당 연산자가 가장 낮은 우선순위를 갖게 된다는 것은 그리 놀라운 일이 아닙니다. 물론 상기 표에 나타난 연산자의 우선순위를 기억해야 할 필요는 없습니다. 다른 대부분의 개발자들 역시 표의 내용을 기억하는 것이 아닌 필요에 따라 괄호를 사용해 원하는 만큼의 우선순위를 지정하는 방법을 사용합니다.
5. 복합할당연산자
C#에서는 산술이나 논리연산에서 할당을 결합하기 위한 몇 가지 연산자를 제공하고 있습니다. 이들은 주로 하나의 피연산자에 저장될 수 있는 결과를 수행하기 위해 사용되는데 예를 들어 다음과 같은 표현식이 작성되는 경우
x = x + y;
변수 x에 포함되어 있는 값을 y변수의 값에 더하고 그 결과를 다시 x변수에 저장하고 있습니다. 이는 아래와 같이 증감 복합 연산자를 사용하여 간소화할 수 있습니다.
int x = 10;
int y = 20;
x += y;
상기 표현식은 이전과 동일한 작업을 수행하지만 훨씬 더 간략한 방법으로 구현할 수 있음을 알 수 있습니다. 이는 C Programming언어로부터 C#이 물려받은 또 다른 기능 중 하나입니다. 이 외에도 다양한 복합 할당연산자를 C#에서 사용할 수 있습니다. 아래 표에서는 가장 자주 사용되는 복합 할당 연산자를 확인할 수 있습니다.
x += y | x에 y를 더하고 그 결과를 x에 저장 |
x -= y | y에서 x를 감산하고 그 결과를 x에 저장 |
x *= y | x와 y를 곱하고 그 결과를 x에 저장 |
x /= y | x와 y를 나누고 그 결과를 x에 저장 |
x %= y | x와 y에 대한 나머지 연산을 수행하고 그 결과를 x에 저장 |
x &= y | x와 y에 대한 AND 논리 연산의 결과를 x에 할당 |
x |= y | x와 y에 대한 OR 논리 연산의 결과를 x에 할당 |
x ^= y | x와 y에 대한 Exclusive OR연산의 결과를 x에 할당 |
6. 증감 / 감소 연산자
C#에서 사용할 수 있는 또 다른 유용한 연산자로 증감과 감소연산자가 있습니다. 해당 연산자에 대해서는 위에서 복합할당연산자를 설명할 때와 비슷하게 아래와 같은 C# code를 참고할 수 있습니다.
x = x + 1;
y = y - 1;
이들 표현식은 x의 값에 1을 더하고 y의 값에 1을 빼는 것입니다. 이러한 방식의 접근법 대신 우리는 ++와 --연산자를 사용하여 Code를 더욱 간소화할 수 있는데 그 방법을 아래 예제에서 확인할 수 있습니다. 아래 예제는 상기 예제와 정확히 동일한 동작을 수행합니다.
x++;
y--;
또한 증감/감소 연산자는 변수의 앞이나 뒤에 올 수 있는데 이러한 위치에 따라 결과가 달라질 수 있습니다. 연산자가 변수의 앞에서 사용되는 경우 다른 연산자가 수행되기 전 증감/감소에 대한 동작을 해당 변수에 대해서 먼저 수행하게 됩니다. 예를 들어 아래 예제에서 x는 변수 y에 할당이 이루어지기 전 증감을 먼저 수행하게 되므로 y의 값은 2가 됩니다.
int x = 1;
int y = ++x;
반면 아래 예제에서 y는 감소연산자가 변수에 대해 감산동작을 먼저 수행하기 전에 할당이 이루어지므로 y의 값은 1이 되고 x의 값은 0이 됩니다.
int x = 1;
int y = x--;
7. 비교 연산자
산술 및 할당연산자와 더불어 C#에서는 비교를 수행하기 위한 일련의 논리연산자를 포함하고 있습니다. 이들 연산자는 모두 비교를 수행한 이후 그 결과에 대한 bool(true/false) 값을 반환하며 2개의 피연산자로 작동한다는 점에서 Binary연산자에 해당하기도 합니다.
비교연산자는 특히 Program의 흐름을 제어하는데 가장 자주 사용되는 것으로 예를 들어 아래와 같은 if문의 경우처럼 하나의 값이 다른 값과 일치하는지의 여부를 판단하기 위해 만들어질 수 있습니다.
int x = 10;
int y = 10;
if (x == y)
Console.WriteLine("x is equal to y");
위 실행결과는 다음과 같습니다.
x is equal to y |
필요하다면 위와 같은 비교의 결과는 bool형식의 변수에 저장할 수도 있을 것입니다. 아래의 예제에서는 result라는 이름의 변수에 true값을 저장하는 예제를 표현하고 있습니다.
int x = 10;
int y = 20;
bool result = x < y;
명확하게 x의 값은 y값보다 작습니다. 따라서 x < y 표현 식이 true로 평가됩니다. 아래 표를 통해서는 C#에서 비교연산이 수행되는 방식을 나열하였습니다.
x == y | x가 y와 같으면 true입니다. |
x > y | x가 y보다 크면 true입니다. |
x >= y | x가 y보다 크거나 같으면 true입니다. |
x < y | x가 y보다 작으면 true입니다. |
x <= y | x가 y보다 크거나 같으면 true입니다. |
x != y | x와 y의 값이 다르면 true입니다. |
8. Boolean 논리 연산자
Boolean유형의 true나 false값을 반환하는 것으로 C#에서 사용할 수 있는 또 다른 논리연산자로 Boolean 논리 연산자가 있습니다. 이들 연산자는 Boolean결과를 반환할 뿐 아니라 Boolean값의 피연산자를 필요로 합니다. 가장 일반적으로 사용되는 연산자는 NOT(!), AND(&&), OR(||)그리고 XOR(^)이 있습니다.
NOT(!) 연산자는 현재의 Boolean값 또는 표현식의 결과를 뒤집는 연산자입니다. 예를 들어 예제의 b이름의 변수는 현재 true인데 앞에 !문자를 통해 NOT연산을 수행하게 되면 값이 false로 반대가 됩니다.
bool b = true;
bool result = !b; //false;
OR(||) 연산자는 대상이 되는 피연산자 중 하나라도 true로 평가되면 true를 반환하며 그렇지 않은 경우 false를 반환합니다. 예를 들어 아래 예제의 경우에는 양측 OR연산자의 표현식에서 최소 하나가 true이므로 최종적으로 true로 평가됩니다.
if ((10 < 20) || (20 < 10))
Console.WriteLine("Result : true");
AND(&&) 연산은 오로지 대상이 되는 피연산자가 true로 평가되어야 true를 반환합니다. 예를 들어 아래 예제의 경우 2개의 피연산자 중 하나만이 true로 평가되므로 결과를 false를 반환합니다.
if ((10 < 20) && (20 < 10))
Console.WriteLine("Result : false");
XOR(^) 연산은 오로지 대상이 되는 피연산자 중 하나만 true로 평가되어도 true를 반환합니다. 예를 들어 아래 예제의 경우 단 하나의 연산자가 true가 되므로 결과는 true가 됩니다.
if ((10 < 20) ^ (20 < 10))
Console.WriteLine("Result : true");
만약 상기 예제에서 2개의 피연산자가 모두 true나 혹은 false로 평가된다면 표현식은 false를 반환할 것입니다.
9. 범위와 Index 연산자
범위와 Index연산자를 사용하면 값의 범위를 선언할 수 있으며 이러한 방법은 Array와 같은 Collection과의 작업에서 매우 유용하게 사용됩니다. 해당 연산자에 관해서는 별도로 다루도록 하겠습니다.
10. 삼항 연산자
삼항연산자를 사용하면 true나 false에 따라 빠르게 다른 결과를 얻을 수 있습니다. 삼항 연산자의 문법은 아래와 같습니다.
[조건] ? [true 표현식] : [false 표현식] |
여기서 [조건] 부분은 true나 false를 반환하는 표현식으로 대체될 수 있습니다. 이때 결과가 true가 되면 [true 표현식]에 있는 표현식이 평가되며 그렇지 않으면 [false 표현식]이 평가됩니다.
int x = 10;
int y = 20;
Console.WriteLine(x < y ? y : x);
위 예제는 간단히 큰 값을 출력하도록 하는 것으로 실행결과는 20이 됩니다. 여기서 [조건] 표현식은 true와 false를 반환하는 표현식이면 어떤 것이든 유효하며 그에 따른 true/false표현식 또한 단일문으로 평가될 수 있는 것이라면 어떤 것이든 유효합니다. 따라서 위 예제를 아래와 같이 각 결과에 대해서 특정 문자열로서 표현하도록 하는 경우도 정상적으로 처리될 수 있습니다.
int x = 10;
int y = 20;
Console.WriteLine(x < y ? "y is larger" : "x is larger");
11. NULL 병합 연산자
NULL 병합 연산자는 피연산자가 NULL값을 가지게 되는 경우 해당 유형에 맞는 다른 값을 대신 사용할 수 있도록 해주는 연산자이며 아래와 같은 문법으로 사용할 수 있습니다.
<피연산자> ?? <사용될 기본값> |
위 문법에서는 피연산자가 NULL값이 될 때 오른쪽에 지정한 기본값을 대신 사용하도록 합니다. 그렇지 않고 만약 피연산자가 NULL이 아닌 값을 가지면 해당 피연산자의 값을 반환합니다.
아래 예제의 경우 Cty변수는 NULL상태이므로 결과적으로 Welcome To Korea!라는 문자열을 출력하게 될 것입니다.
string? cty = null;
string wcmsg = "Welcome to " + (cty ?? "Korea!");
Console.WriteLine(wcmsg);
이 외에도 C#에서는 NULL 병합 할당 연산자(??=)를 사용하여 피연산자에 특정한 값을 할당할 수도 있습니다.
<피연산자> ??= <할당값> |
왼쪽 피연산자가 NULL인 경우 오른쪽 값을 왼쪽 피연산자에 할당하게 되며 그렇지 않으면 왼쪽 피연산자의 값은 바뀌지 않습니다. 이때 <할당값>에는 Literal값이나 다른 변수가 올 수 있습니다.
아래 예제의 경우 cty에 초기값을 할당하고 있으므로 cty의 값은 바뀌지 않을 것입니다.
string? cty = "Korea!";
cty ??= "US";
string wcmsg = "Welcome to " + cty;
Console.WriteLine(wcmsg);
12. Bit 연산자
Computer의 처리 단위는 binary(2진수)이며 이것은 0과 1의 연속적인 흐름으로 이루어져 있고 이들 하나하나를 bit로서 다루게 됩니다. 여기서 bit는 1byte 단위, 총 8bit의 묶음으로 형성되어 있습니다. 사실 개발자입장에서 이러한 자료구조를 직접적으로 접하는 경우는 흔하지 않지만 아주 가끔은 bit단위의 연산을 수행해야 하는 경우도 있는데 C#에서는 이러한 작업의 필요성에 대응하기 위해 일련의 bit연산자를 제공하고 있습니다.
C나 C++과 같은 언어에서 bit연산자는 꽤 자주 사용되는데 binary 수에 익숙하지 않다면 이 주제에 대한 다른 자료를 참고하여 특정 값을 형성할 때 byte에서 어떻게 0과 1이 형성되는지를 알아보는 것도 좋을 것입니다. 지금 여기서는 배가 산으로 갈 수 있기에 해당 주제에 대해 더 나아가지는 않을 것입니다.
이제 bit연산자를 실제 사용해 보기 위해 2개의 숫자를 2진법으로 바꿔보도록 하겠습니다.(좀 더 간결함을 위해 8bit를 사용합니다.)
우선 십진 수 171을 2진법으로 표현하면 다음과 같습니다.
10101011 |
두 번째 숫자 3을 2진법으로 표현하면 다음과 같습니다.
00000011 |
이제 이 값을 대상으로 bit연산자를 적용해 보겠습니다.
(1) Bitwise NOT
Bitwise NOT연산자는 tilde(~) 문자로 나타내며 모든 bit를 반전시키는 효과를 가져옵니다. 다시 말해 모든 0은 1로, 모든 1은 0으로 바뀌게 됨을 의미합니다. 예를 들어 이진수 3에 Bisewise NOT연산자를 적용하면 아래와 같은 결과를 생성하게 됩니다.
00000011 => NOT적용 결과 11111100 |
C# Code로 나타낸 아래 예제에서도 동일하므로 결과는 -4가 됩니다.
int x = 3;
int y = ~x;
Console.WriteLine($"{y}"); //-4
(2) Bitwise AND
Bitwise AND는 ampersand(&) 문자로 나타내며 두 수의 bit를 비교하여 같은 위치의 bit가 모두 1일 때만 1을 생성하는 연산자입니다. 따라서 해당 위치의 bit가 하나라도 0이라면 결과는 0이 됩니다.
10101011 00000011 => AND적용 결과 00000011 |
위의 결과 역시 마지막 2개의 bit만이 모두 1이 되므로 결과도 마지막 bit가 1인 결과가 만들어지게 됩니다. 해당 과정과 동일한 식을 C# Code로 나타내는 아래 예제에서도 결과는 3이 됩니다.
int x = 171;
int y = 3;
int z = x & y;
Console.WriteLine($"{z}");
(3) Bitwise OR
bitwise OR도 2개의 이진수에 대한 bit단위 비교를 수행합니다. 다만 AND와는 달리 OR는 비교대상 피연산자 중 하나라도 1이면 결과는 1이 됩니다. Bitwise OR는 '|'문자로 나타내며 아래 예제를 통해 연산의 결과를 확인하실 수 있습니다.
10101011 00000011 => OR연산 10101011 |
위 예제를 C#으로 나타내면 다음과 같이 표현할 수 있습니다.
int x = 171;
int y = 3;
int r = x | y;
Console.WriteLine($"{r}");
(4) Bitwise XOR
Bitwise XOR는 일반적으로 배타적 OR연산으로 표현되며 Caret(^) 문자로 표현됩니다. OR연산과 비슷하지만 비교대상 피연산자 중 하나 또는 다른 bit의 위치가 1이면 결과는 1이 됩니다. 피연산자의 해당 bit위치가 모두 1이거나 0이라면 결과는 0이 됩니다.
10101011 00000011 => XOR 10101000 |
위 예제의 결과는 10101000으로서 10진수 168이 되며 아래와 같이 C#으로 나타낼 수 있습니다.
int x = 171;
int y = 3;
int r = x ^ y;
Console.WriteLine($"{r}");
(5) Bitwise Left Shift
Bitwise Left Shift는 Binary에서 각 bit를 특정한 수의 위치만큼 왼쪽으로 이동시키는 연산자입니다. 이때 왼쪽으로 1만큼 이동하는 것은 값을 2배로 올리는 효과를 가지게 됩니다.
왼쪽으로 bit가 이동함으로써 비워진 가장 오른쪽에는 0이 채워지고 가장 왼쪽에 위치한 bit의 경우 값을 가진 변수의 범위를 넘어서게 되면 버려진다는 것에 주의해야 합니다.
10101011 => Bitwise Left Shift 적용 101010110 |
C#에서 Bitwise Left Shift연산자는 '<<'로 나타내며 이를 Code로 적용하면 다음과 같이 표현할 수 있습니다.
int x = 171;
int r = x << 1;
Console.WriteLine($"{r}");
상기 예제를 compile 하고 실행하는 경우 342라는 값을 출력하게 되며 이는 binary로 101010110과 동일합니다.
(6) Bitwise Right Shift
Bitwise Right Shift는 Bitwise Left Shift와 반대로 동작한다는 점을 제외하면 Bitwise Left Shift와 동일한 개념을 가집니다. 따라서 bit의 위치가 오른쪽으로 이동하면 이로 인해 값은 절반씩 줄어드는 효과를 가지게 됩니다.
bit를 오른쪽으로 이동시키기 때문에 가장 오른쪽 bit는 버려진다는 점에 주의해야 하며, 이동 후 가장 왼쪽의 bit는 설정값이 음수인지를 나타내는 부호가 사용되었는가에 따라서 0 또는 1로 대체될 수 있습니다.
10101011 => Bitwise Right Shift 적용 01010101 |
C#에서 Bitwise Right Shift연산자는 '>>'로 나타내며 이를 Code로 적용하면 다음과 같이 표현할 수 있습니다.
int x - 171;
int r = x >> 1;
Console.WriteLine($"{r}");
상기 예제를 compile 하고 실행하는 경우 85라는 값을 출력하게 되며 이는 binary로 01010101과 동일합니다.
13. 복합 Bitwise 연산자
산술연산자와 동일하게 bit단위 연산자도 해당 복합연산자를 가질 수 있으며 이를 통해 연산과 할당을 단일 연산자를 통해서 이루어질 수 있도록 구현할 수 있습니다. 자세한 사항은 아래 표를 참고하시기 바랍니다.
x &= y | x와 y에 대한 AND연산을 수행하고 그 결과를 x에 할당합니다. |
x |= y | x와 y에 대한 OR연산을 수행하고 그 결과를 x에 할당합니다. |
x ^= y | x와 y에 디한 XOR연산을 수행하고 그 결과를 x에 할당합니다. |
x <<= n | n만큼 x에 대한 Left연산을 수행하고 그 결과를 x에 할당합니다. |
x >>= n | n만큼 x에 대한 Right연산을 수행하고 그 결과를 x에 할당합니다. |