선택문
- C#의 선택문(selection statements)으로 if와 switch 구문이 있다.
- 두 단어 모두 C#의 예약어이며 그에 따른 일정한 형식의 문법이 있다.
- bool 자료형이 나오는 C# 연산자를 알아야 하는데, 전형적인 선택문에는 반드시 '조건'이 따르게 되고, 그 조건의 평가는 참/거짓으로 나오기 때문이다.
관계 연산자, 논리 연산자
연산의 결과가 참/거짓으로 나오는 C#의 연산자로는 관계 연산자(relational operator)와 논리 연산자(logical operator)가 있다.
6가지 관계 연산자
관계 연산자 | 평가 방식 | 예제 |
> | 좌측 피연산자가 우측 피연산자보다 크면 참, 같거나 작으면 거짓 | bool result = 10 > 20; //거짓 |
< | 좌측 피연산자가 우측 피연산자보다 작으면 참, 같거나 크면 거짓 | bool result = 10 < 20; //참 |
>= | 좌측 피연산자가 우측 피연산자보다 크거나 같으면 참, 작으면 거짓 | bool result = 10 >= 20; //거짓 |
<= | 좌측 피연산자가 우측 피연산자보다 작거나 같으면 참, 크면 거짓 | bool result = 10 <= 20; //참 |
== | 좌측 피연산자가 우측 피연산자와 같으면 참, 다르면 거짓 | bool result = 10 == 20; //거짓 |
!= | 죄측 피연산자와 우측 피연산자가 다르면 참, 같으면 거짓 | bool result = 10 != 20; //참 |
조건식을 여러 개 나열도 가능하다.
개별 조건식의 결관는 반드시 bool 타입이므로 조건식을 나열한다는 것은 곧 부울 대수(boolean algebra)의 기본 연산을 하는 것과 같다.
이때 사용되는 것이 C#의 논리 연산자로서, &&(AND: 논리곱), ||(OR: 논리합), !(NOT: 부정), ^(XOR: 배타적 논리합)의 4가지가 여기에 해당된다.
논리곱 연산자 &&(AND)
좌측 피연산자 | 우측 피연산자 | AND 연산 결과 | 예제 |
true | true | true | bool result = true && true; |
true | false | false | bool result = true && false; |
false | true | false | bool result = false && true; |
false | false | false | bool result = false && false; |
논리합 연산자 ||(OR)
좌측 피연산자 | 우측 피연산자 | OR 연산 결과 | 예제 |
true | true | true | bool result = true || true; |
true | false | true | bool result = true || false; |
false | true | true | bool result = false || true; |
false | false | false | bool result = false || false; |
배타적 논리합 연산자 ^(XOR)
좌측 피연산자 | 우측 피연산자 | XOR 연산 결과 | 예제 |
true | true | false | bool result = true ^ true; |
true | false | true | bool result = true ^ false; |
false | true | true | bool result = false ^ true; |
false | false | false | bool result = false ^ false; |
부정 연산자(NOT)
우측 피연산자 | NOT 연산 결과 | 예제 |
true | false | bool result = !true; |
false | true | bool result = !false; |
논리 연산자를 사용할 때 주의할 점
- 부울 대수에 따라 실행할 때 단락 계산(short-circuit)이 두가지 상황에서 발생할 수 있다는 점이다.
ex) || 논리합 연산을 하다고 가정
이때 이미 (n1 % 3 == 0)의 표현식이 참이기 때문에 뒤의 (n2 % 3 == 0)의 결과값에 상관없이 전체 평가식이 참이 된다.
이런 이유로 프로그램의 실행 과정에서 뒤의 조건은 아에 실행조차 되지 않는 것을 두고 단락 계산 또는 단축 평가(short-circuit evaluation)됐다고 한다.
||(OR) 연산과 마찬가지로 단락 계산은 && 논리곱 연산에도 발생한다.
int n1 = 10;
int n2 = 6;
bool result = (n1 % 3 == 0 && n2 % 3 == 0); //result == False
(n1 % 3) == 0의 표현식이 거짓이므로 논리곱 연산의 성격상 뒤에 오는 식에 상관없이 전체 평가식이 거짓이 된다.
(n2 % 3 == 0) 코드는 실행되지 않는다.
if문
- C#의 if문은 괄호 안에 조건식을 지정하고 그것의 평가 결과가 참이면 괄호 다음의 코드를 실행하고 거짓이면 괄호 다음의 코드를 실행하지 않는다.
if문의 조건이 참인 경우 여러 개의 구문을 실행해야 할 경우?
- 블록(block)이라는 새로운 문장부호를 사용한다.
- 블록은 시작과 끝을 지정하기 위해 중괄호(brace)를 사용하는데, 기본적으로 if문은 다음과 같은 중괄호를 사용한다.
if(6 % 3 == 0)
{
Console.WriteLine("6은 3의 배수");
}
- 블록 안에는 0개 이상의 구문이 올 수 있으며, 순차적으로 실행된다.
- 여러개의 구문을 실행해야 한다면 블록 내에 원하는 코드를 적절한 순서에 맞게 추가하면 된다.
if(6 % 3 == 0)
{
Console.WriteLine("6은 3의 배수");
Console.WriteLine("연산 결과 끝");
}
- if문은 조건식의 결과가 참인 경우뿐만 아니라 거짓인 경우에도 실행할 수 있는 구문을 else 예약어를 추가해 지정할 수 있다.
- 조건이 여러 개인 경우 if/else if/ else로 표현하는 것도 가능하다.
C#에는 if문의 한 가지 전형적인 사용 사례에 대해 간단하게 줄여서 작성할 수 있게 연산자 차원에서 지원하고 있다.
이것을 조건 연산자(또는 삼항 연산자)라고 한다.
(조건식) ? 표현식1 : 표현식2;
조건식이 참인 경우 표현식1을 평가해서 반환하고, 거짓인 경우 표현식2를 평가해서 반환한다.
이 조건을 삼항 연산자(ternary operator)를 이용해 간단하게 표현할 수 있다.
조건 연산자를 삼항 연산자라고 부르는 이유 - 피연산자(operand)가 3개이기 때문이다.
- 첫 번째 피연산자는 조건식을 나타내고, 두번째 피연산자는 조건식이 참인 경우 반환되는 값, 세 번째 피연산자는 조건식이 거짓인 경우에 반환되는 값을 나타낸다.
Switch 문
- switch문은 if문의 특수한 형태에 해당한다. 여러 개의 조건을 판단해서 실행할 때 if/else if/ else if/.../else 구문을 쓸 수도 있지만, 조건 판단의 기준이 되는 식이 상수라면 switch 구문을 쓰는 것이 편리할 수 있다.
- 모든 switch문은 다중 if문으로 변환할 수 있지만, 특정한 경우의 다중 if문만이 switch문으로 변환될 수 있다.
- 조건이 몇 개 안되는 경우 if문을 쓰는 것과 별 다른 차이를 느낄 수 없지만, 조건이 많아지면 switch 문을 쓰는 편이 직관적인 코드 해석에 도움이 된다.
- C#에서는 case 문에 break를 포함하는 것이 강제 사항이라서 break가 없으면 컴파일할 때 오류가 발생한다. 대신 case 문에 실행할 코드가 포함되어 있지 않다면 break를 생략하는 것도 가능하다.
- switch 문에서 마지막으로 언급할 사항은 default 구문이 강제 사항이 아니다. 필요하지 않다면 생략해도 무방하다.
string text = "C#";
switch(text)
{
case "C#":
Console.WriteLine(".NET 호환 언어");
break;
case "JAVA":
Console.WriteLine("JVM 언어");
break;
}
반복문
- C#에서는 for, foreach, while, do/while의 4가지 반복문을 제공하지만, if 문과 switch의 관계처럼 각 반복문은 서로 다른 반복문으로 자연스럽게, 때로는 다소 억지스럽게 변환하는 것이 가능하다.
- 참고로 반복문은 루프(loop)라고도 한다.
증감 연산자, 복합 대입 연산자
- 반복문은 보통 조건을 포함한다.
- 반복문은 탈출 조건을 만족하기 위해 변수i의 값을 변화시키는 구문을 포함하게 되는데, 이때 사용되는 것이 '증감 연산자'와 '복합 대입 연산자'이다.
증감 연산자(increment & decrement operator)
증감 연산자 | 평가 방식 | 예제 |
++ | 피연산자의 값을 1 증가시킨다. | int n = 50; n++; //결과값 51 |
-- | 피연산자의 값을 1 감소시킨다. | int n = 50; n--; //결과값 49 |
- 증감 연산자에서 특별히 알아둬야 할 것이 있다면 바로 전위(prefix)/후위(postfix) 표기법이다.
- 즉, ++와 --연산자가 피연산자의 앞에 오느냐 뒤에 오느냐에 따라 동작 방식이 바뀐다는 점이다.
int n = 50;
n++; //증가 연산자 후위 표기법
n--; //감소 연산자 후위 표기법
++n; //증가 연산자 전위 표기법
--n; //감소 연산자 전위 표기법
- 후위 표기법은 피연산자 값이 평가된 후 값을 증가/감소시키는 것이고, 전위 표기법은 피연산자의 값을 증가/감소시킨 후에 식을 평가한다.
- 대입 연산자와 함께 증감 연산자가 사용되는 경우에도 같은 결과 확인이 가능하다.
int n = 50;
int result;
result = n++; //result에 50을 대입한 후 값을 51로 증가, result의 값은 50
n = 50;
result = ++n; //n의 값을 51로 증가시킨 후에 result 값을 대입, result의 값은 51
n = 50;
result = n--; //result에 50을 대입한 후에 값을 49로 감소, result의 값은 50
n = 50;
result = --n; //n의 값을 49로 감소한 후에 result 값을 대입, result의 값은 49
- 논리연산자와 증감 연산자를 함께 사용할 때는 주의할 필요가 있다.
int n = 50;
int x = 100;
if(x >10 || n ++ > 10)
{
...
}
실제로 프로그램을 실행시켜 보면 n은 언제나 50에 머문다.
이유 : 논리합의 좌측 피연산자에 해당하는 식이 참으로 평가되기 때문에 우측의(n++ > 10)코드는 절대 실행되지 않는 단락 계산이 발생하기 때문이다.
이런식의 코드를 만드는 경우 프로그램의 오동작을 일으키는 원인이 될 수 있으므로 논리 연산자의 피연산자에 해당하는 식에는 가능한 증감 연산자를 함께 사용하지 않고, 명시적으로 분리해서 처리하는 것이 바람직하다.
int n = 50;
int x = 100;
if(x >10 || n ++ > 10)
{
...
}
n ++;
증감 연산자는 값을 +1/-2만큼 바꾸는데, 그 이상의 값을 증가/감소시키는데 유용한 표현으로 복합 대입 연산자(compound assignment operator)가 있다.
연산자 | 평가 방식 | 예제 |
+= | 우측 피연산자의 값을 좌측 피연산자 값에 더해 그 결과를 좌측 피연산자에 대입한다. | int n = 50; n += 5;//결과값 55 |
-= | 우측 피연산자의 값을 좌측 피연산자 값에 서 빼고 그 결과를 좌측 피연산자에 대입한다. | int n = 50; n-=5;//결과값 45 |
*= | 우측 피연산자의 값을 좌측 피연산자 값에 곱해 그 결과를 좌측 피연산자에 대입한다. | int n = 50; n *= 5;//결과값 250 |
/= | 우측 피연산자의 값으로 좌측 피연산자 값을 나눠 그 결과를 좌측 피연산자에 대입한다. | int n = 50; n /= 5;//결과값 10 |
%= | 우측 피연산자의 값으로 좌측 피연산자 값의 나머지 값을 구하고 그 결과를 좌측 피연산자에 대입한다. | int n = 50; n %= 5; //결과값 0 |
- 대입 연산자는 단순 대입 연산자(simple assignment operator)와 복합 대입 연산자로 나뉜다. 단순 대입 연산자로는 '='이 있고, 그 밖의 모든 대입 연산자는 복합 대입 연산자에 속한다.
복합 대입 연산자는 사실 단순 대입 연산자를 사용한 시그이 단축 표현이다.
복합 대입 연산자 표현 | 단순 대입 연산자 표현 |
n += 5; | n = n+5; |
n -= 5; | n = n-5; |
n *= 5; | n = n*5; |
n /= 5; | n = n/5; |
n %= 5; | n = n%5; |
for 문
- for문의 괄호 안에는 2개의 세미콜론(;)을 구분자로 해서 3개의 코드를 넣을 수 있는데, 첫 번째는 반복을 시작하기에 앞서 초기화가 필요한 코드를, 두 번째는 for문이 반복을 유지할 수 있는 조건식을, 세 번째는 for 문으로 인해 반복되는 구문이 실행을 완료할 때마다 자동으로 실행되는 코드를 넣는다.
위의 코드가 실행되는 순서
- n = 1 초기화 코드 수행
- n <= 9 조건식 평가(n의 값이 1이므로 true를 반환)
- for문이 포함하는 구문 코드 수행: 여기서는 Console.WriteLine
- n++ 반복식 코드 수행
- n <= 9 조건식 평가(n의 값이 2이므로 여전히 true를 반환)
- 3 ~ 5번 과정을 n의 값이 9가 될 때까지 반복, n의 값이 10이 되면 for문을 벗어난다.
특이하게도 초기화, 조건식, 반복식이모두 선택사항이라 각 부분에 해당하는 코드를 제거할 수 있다.
int n = 1;
for(; n <=9; n++)
{
Console.WriteLine(n);
}
조건식도 생략 시 if문으로 조건식 대체 가능
int n = 1;
for(; ; n++)
{
if(n>9) break;
Console.WriteLine(n);
}
switch문에서 본 break를 for문에서 사용할 수 있는데, 이 경우 for문을 벗어나는 역할을 한다.
마지막으로 반복문 코드를 for 괄호 안에서 제거한 경우
int n = 1;
for(;;)
{
if(n > 9) break;
Console.WriteLine(n);
n ++;
}
이렇게 for문의 괄호 안에 있던 3개의 코드를 모두 제거할 수 있지만, 세미콜론(;) 구분자는 여전히 남아있어야 한다.
세미콜론마저 생략 시 컴파일 과정에서 오류 발생.
중첩 루프
- 제어문은 기본적으로 구문 하나를 제어할 수 있지만 블록을 사용하는 경우 여러 개의 구문을 제어할 수 있다.
- 그 구문에는 다시 제어문이 포함될 수 있는데, for문 루프 안에 또 다시 for 루프가 있다면 이를 '중첩 루프(nested loop)'라고 한다.
- 중첩 루프를 이용하면 구구단 같은 계산을 쉽게 할 수 있다.
for(int x = 2; x < 10; x ++)
for(int y = 1; y<10; y++)
Console.WriteLine(x + " * " + y + " = " + (x * y));
//또는 실행할 구문이 하나인 경우에도 다음과 같이 가독성을 높이기 위해
//블록을 사용하기도 한다.
for(int x = 2; x < 10; x++)
{
for (int y = 1; y < 10; y++)
{
Console.WriteLine(x + " * " + y + " = " + (x * y));
}
}
foreach 문
foreach문은 for문과 이름은 비슷하지만 문법은 전혀 다르다.
foreach(표현식요소의_자료형 변수명 in 표현식)
구문;
//또는
foreach(표현식요소의_자료형 변수명 in 표현식) 구문;
//표현식에 올 수 있는 대표적인 자료형은 배열이다.
//배열인 경우 배열의 요소 수만큼 구문이 반복된다.
//각 반복마다 배열 요소의 값을 변수명에 넣어서 실행한다.
//표현식에 컬렉션 자료형도 사용할 수 있다.
foreach문은 in 다음에 오는 배열을 처음부터 끝까지 순회하면서 개별 요소를 int elem으로 선언된 변수에 넣어 반복문 구문 내에서 해당 변수를 사용할 수 있게 해준다.
foreach문 역시 for문 그대로 변경할 수 있다.
int[] arr = new int[] { 1, 2, 3, 4, 5 };
for (int i = 0; i < 5; i++)
{
Console.WriteLine(arr[i] + ",");
}
일반적으로 foreach문이 for문보다 구문이 간결하기 때문에 더 자주 사용된다.
while문
- 간편하게 조건식만 있는 반복문이 필요할 때 사용하는 것이 while 반복문이다.
while(조건식)
구문;
또는
while(조건식) 구문;
//조건식을 평가하고 참이면 구문을 실행한다.
//실행 순서 : 조건식 -> 구문 -> 조건식 -> 구문 ... 으로 조건식이 거짓이 될 때까지 무한으로 반복된다.
for문을 사용해서 구현할 수도 있다.
int sum = 0;
for(int n = 1; n<=1000; n++)
{
if(n % 2 == 0)
{
sum += n;
}
}
while 문의 경우 유사하게 do/while 반복문도 제공된다.
do
구문;
while(조건식);
//또는
do 구문 while(조건식);
// 먼저 구문을 실행하고 조건식을 평가한다.
// 실행 순서: 구문 -> 조건식 -> 구문 -> 조건식 ... 으로 조건식이 거짓이 될 때까지 무한 반복된다.
// while문과는 다르게 do/while 문은 반복 실행되야 할 구문이 최소 한 번은 실행된다.
변수를 증가시키는 반복 구조에서 초기 변수값을 어떻게 초기화하느냐 정도의 차이만 있을 뿐 while문으로 작성한 코드를 그대로 옮길 수 있다.
int sum = 0;
int n = 0; //초기값이 0으로 변경
do
{
if (n % 2 == 0) sum += n;
}
while (++n <= 1000);
- 이 처럼 각 반복문이 다른 반복문으로 변환될 수 있다는 점 때문에 어떤 유형의 문제를 풀때는 어떤 구문의 반복문을 반드시 사용해야 한다는 식의 선택 기준은 없다.
- 가독성이 가장 높은 유형의 반복문을 선택해서 문제를 해결하는 것이 권장된다는 정도의 암묵적인 권고 사항만 있다.
점프문
- C#에서 제공되는 점프문(jump statments)으로는 break, continue, goto, return, throw가 있다.
break 문
- break문은 switch와 for/foreach/while/do 반복문 내에서만 사용할 수 있다.
- switch에서 사용하면 해당 case 실행을 벗어나는 역할을 하고, 반복문 내에서 사용하면 break를 둘러싼 첫 번째 반복문을 탈출한다.
continue 문
- break 문이 루프를 벗어나는 반면, contine 문은 이후의 반복 구문 실행을 생략하고 바로 조건식 평가로 실행을 옮기면서 반복을 계속한다.
int sum = 0;
int n = 1; //초기값이 0으로 변경
while(n++ <= 1000)
{
if ((n % 2) != 0)
{
continue; //sum+= n; 구문을 건너뛰고, while 문의 조건식 평가로 실행을 옮긴다.
}
sum += n;
}
- break와 continue는 가독성을 높이기 위해 들여쓰기 블록을 줄이는 역할도 한다.
- 복잡한 프로그램을 만들다 보면 블록이 중첩되는 경우가 있는데, 가독성 측면에서 볼 때 그다지 좋은 코드라고 할 수 없다.
- 이러한 경우 continue 문을 이용하면 좀 더 구조적인 코드를 만들 수 있다.
int sum = 0;
int n = 1; //초기값이 0으로 변경
while(n ++ <= 1000)
{
if ((n % 2) != 0) continue;
if ((n % 3) != 0) continue;
if ((n % 5) != 0) continue;
sum += n;
}
- continue 문을 적절히 사용하면 눈으로 따라가서 추적해야 하는 '들여쓰기', '블록'의 수를 줄일 수 있으므로 코드를 읽고 이해하기 더 쉬워진다.
goto문
- goto문과 if 문만 있으면 반복문(for, foreachm while, do)을 모두 대체할 수 있다.
- 실제로 초기의 어셈블리 언어로 프로그램을 만들면 오직 goto/if 문으로만 반복을 구현할 수 있었다.
- goto는 사실상 제어문의 원조격이며, 세월이 지나 다른 구조적인 구문이 나오면서 홀대받는 신세로 전락했다.
- goto만으로 프로그램을 작성하는 경우 코드의 가독성이 현저히 떨어지는 문제가 발생했기 때문이다.
goto를 사용하려면 제어 흐름이 옮겨지는 대상을 구분하기 위해 레이블 문이 있어야 한다.
레이블 문은 식별자에 콜론(:)을 붙여서 만드는데, 관례상 대문자로만 쓰는 것이 일반적이다.
goto문을 쓰면 얼마나 가독성이 떨어지는가?
int sum = 0;
int n = 0;
LOOP:
n++;
if(n > 1000)
{
goto LOOPEXIT;
}
if((n % 2) != 0) goto LOOP;
sum += n;
goto LOOP;
LOOPEXIT:
Console.WriteLine(sum);
코드의 구조가 파악하기 쉽지 않다.
이런 이유로 인해 goto는 절대로 써서는 안되는 것으로 단정 짓는 개발자들도 있다고 한다.
현재 유일하게 goto문이 유용하다고 합의를 보는 사례
'중첩 루프에서 탈출'하는 경우에 한해서 사용한다.
for(int x = 2; x < 10; x++)
{
for(int y = 1; y < 10; y++)
{
Console.WriteLine(x + " * " + y + " = " + (x * y));
if (x == 5 && y == 8) goto LOOP_EXIT;
}
}
LOOP_EXIT: ;
정리
컴퓨터 과학에서 '특정 문제를 푸는 데 사용하는 일련의 코드 모음'을 알고리즘이라는 말로 표현한다.
- 구구단
- 1 ~ 1,000 범위의 짝수의 합
복잡한 알고리즘은 메서드나 클래스를 이용하면 더 잘 표현할 수 있지만, 일단 데이터형과 제어문의 조합으로도 알고리즘은 어느 정도 표현할 수 있다.
- 1에서 999까지 반복(+1씩 증가, 1,000 보다 작은 자연수이므로 1,000을 포함하지 않음)
- 3과 5의 배수가 아닌면 다음 반복으로 진행
- 그렇지 않으면 모두 더함
- 반복문이 끝났으면 더한 값을 출력
그리고 소스코드가 아닌 순서도를 통해 표현할 수 있다.
예약어, 연산자 문장 부호
예약어 | - sbyte, byte, short, ushort, int, uint, long, ulong - float, double, decimal - char, string - bool - if, else - switch, case, break, default - for, foreach - while, do - continue, goto - new, null |
연산자 | - 관계연산자 : >, <, >=, <=, ==, != - 조건(삼항) 연산자 : ?, : - 논리 연산자 : &&, ||, !, ^ - 산술 연산자 : +, -, *, /, % - 대입 연산자 : =, +=, -=, *=, /=, %= - 증감 연산자 : ++, -- |
문장 부호 | - 배열을 나타내는 대괄호 : [] - 블록을 나타내는 중괄호 : {} - 구문의 끝을 나타내는 ';' |
Reference
시작하세요! C# 12 프로그래밍 기본 문법부터 실전 예제까지
'C#' 카테고리의 다른 글
C# 객체지향 문법 [생성자, 종료자, 정적멤버, 인스턴스 멤버, 네임스페이스] (0) | 2025.05.13 |
---|---|
C# 객체지향 문법 [클래스, 필드, 메서드] (0) | 2025.05.12 |
C# [배열] (0) | 2025.05.07 |
C# [기본 문법 요소] (0) | 2025.05.07 |
C# [형 변환] (0) | 2025.05.05 |