본문 바로가기

별걸다하는 IT/프로그래밍언어

[C/C++/Java 프로그래밍기초]조건문 상세설명 (if else, 삼항연산자, 스위치 switch case 와 if문의 차이점, goto문)

오늘은 코딩의 기초 중에 기초 조건문에 대해서 들고왔습니다.

과장해서 조건문이랑 반복문만 알면 모든 코딩이 가능하다 할 정도로 빼먹을 수 없는 놈인데요

조건문이라 하기도 하고, 제어문이라 하기도 하고 분기문이라 하기도 하고,, 일단 다 같은 말입니다 ㅎㅎ

조건문은 왜 필요해?

간단한 계산기만 만들어보려고 해도,

'+'가 눌렸을 때에는 더해줘야하고 '-'가 눌린다면 요건 빼줘야하고 '*'가 눌리면 곱해줘야 하고~

이렇게 필요에 따라서 덧셈 혹은 뺄셈 또는 어떤 연산자를 선택적으로 실행할 수 있어야 해요.

요렇게 A의 상황에서는 a알고리즘을 적용하고 B의 상황에서는 b알고리즘을 적용하고 싶을 때~~~

즉 상황에 따라서 프로그램의 흐름을 분기시키고 싶을 때 사용되는 명령어가 if 입니다.

if ~ else문

분기의 가장 기본은, 두 개의 키워드 if와 else로 구성이 되는 if~ else문입니다.

if만 독립적으로 사용될 수도 있고 else랑 같이 구성될 수도 있어요 ㅎㅎ

if( num1 > num2 ){ 
    printf("num1이 num2보다 큽니다.); //C언어 출력함수
}

요렇게 if사이에 있는 괄호의 조건이 만족되면, 중괄호에 삽입된 명령이 실행됩니다.

참고로 중괄호안에 있는 명령이 하나라면, 아래처럼 중괄호를 생략해줄 수 있습니다.

if( num1 > num2 )
    System.out.print("num1이 num2보다 큽니다); //Java 출력함수

else

else는 그럼 언제 사용하느냐

else 뜻은 '그 외' 잖아요? 영어 뜻과 동일한 맥락으로 사용돼요 ㅎㅎ 'if 조건이 아닌 그 외 조건'을 말합니다. 

왼쪽 코드랑 오른쪽 코드랑 결국 같은 걸 의미하는거죠 ㅎㅎ

else는 독립적으로 사용되는 것이 아니라 키워드 if와 더불어 하나의 문장을 구성하는 형태로 사용됩니다.

if ( num1 > num2 )
    printf("num1이 num2보다 크다");
else // num1 > num2 가 아닐 때
    printf("num2가 num1보다 크다");

num1 > num2가 충족됐을 땐 if 아래의 명령어를 수행시키고, num1 > num2를 충족시키지 않는 그 외!!즉 if괄호 안의 조건이 참이 아닐 때 (여기서는 num<= num2가 되겠죠? )ㅎㅎ 그 때는 else 밑의 문장을 출력시킵니다.

else if

근데 저렇게 모 아니면 도 말고, 조건이 여러개 일때는 어떻게 하나요?

예를 들어, 성적 90~100점 학생한테는 A학점을 주고 싶고, 80~89 학생한테는 B학점을 주고 싶고, 70~79학생한테는 C 학점을.. 이렇게 여러개로 갈래를 나누고 싶을 때는 if else만으로는 부족하잖아요!?

--> 물론 if문을 여러개 붙여서 만들 수도 있지만, if..else를 이용해서 셋 이상의 블록 중 하나를 선택할 수도 있습니다. 이때 필요한 키워드가 else if!

if(90<=score && score <= 100) //여기서 &&는 '그리고' 를 의미해요 
     grade ='A';
else if (80 <= score && score < 90 )
     grade = 'B';
else if (70 <= score && score <80 )
     grade = 'C';
else 
     grade = 'F';

근데 여러분들 그거 아나요? ㅎㅎ

if 와 else if 그리고 else 요런 형태의 묶음은 사실 위에서 if와 else로만 이루어졌던 if else 이 구문을 중첩시킨 형태에 지나지 않습니다. ㅎㅎ 

 

무슨 말이냐,

if ( num < 0 )
      printf ("num은 음수");
else if ( num > 0)
      printf ("num은 양수");
else 
      printf ("num은 0");

이 코드는 사실 내부적으로 아래처럼 if가 중첩되어 있는 형태로 구성되어 있다는 사실!

if ( num < 0 )
      printf ("num은 음수");
else {
      if ( num > 0 )
           printf ("num은 양수");
      else 
           printf ("num은 0");
}

알고 있었나요?ㅎㅎㅎ

 

참고? 중첩 if문의 순서도 ↓

삼항연산자

그 다음은 if else문을 일부 대체할 수 있는 삼항연산자에 대해서 알아볼게요. 사실 저는 삼항연산자를 즐겨쓰는 편인데요 특히 C언어 할 때는 출력과 함께 활용도가 높은 것 같아요 ㅎㅎ 

일단 삼항연산자 형태를 알아볼까요?

요렇게 생겼어요 ㅎㅎ 코드로 볼까요?

( num1 > num2 )? ( num1 ) : ( num2 );

이게 무엇을 의미하냐면, num1이 num2보다 클 때 num1을 반환하고 그렇지 않을 경우 num2를 반환하라 입니다! 즉 괄호 조건이 참이면 땡땡 (:) 앞 값을 반환하고 그렇지 않을 경우(else), 뒷 값을 반환하라는 거예요 ㅎㅎ

(정확히는 참이면 식1을 수행하고 거짓이면 식2를 수행해라!)

int result = ( num1 > num2 )? num1 : num2;

이렇게 바로 어떤 변수에 값을 넣어 줄 수도 있고

print("%d", (num1>num2)? num1 : num2 ); //C/C++일 경우
System.out.printf("%d", (num1>num2)? num1: num2); //자바일 경우

요렇게도 많이 사용한답니다ㅎㅎ num1이 num2보다 클 경우에는 num1을 출력하고 그렇지 않으면 num2를 출력하라는 뜻이예요 ㅎㅎ 이걸 if else를 사용했때에는 코드가 무려 4줄인데 한 줄로 압축시켰어요!! 코드가 깔끔해집니다. ㅎㅎ

예를 들어, 절대값을 구할 때도 코드를 굳이 여러줄 쓰는 것보다 한 줄에 절댓값을 한 번에 표현하는게 보기에도 해독하기에도 좋겠죠? 아래처럼요!

abs = num>0? num : -num;

switch

프로그램 상 이렇게 조건에 따라 뭔가 알고리즘을 나누고 싶을 때 사용되는 명령어로 또 switch가 있답니다.

if문의 경우, 조건식의 결과가 참과 거짓, 두 가지밖에 없기 때문에 경우의 수가 많아질수록 else-if를 계속 추가해야하므로 조건식이 많아 복잡해지고, 여러 개의 조건식을 계산해야하므로 처리시간도 많이 걸립니다.

이럴 때, 그니까 처리해야할 경우의 수가 많을 때에는 switch문을 많이 써요. 표현이 간결해지거든요 ㅎㅎ

그 외에 스위치와 if문의 각 특징에 따른 차이점은 밑에서 살펴보도록 할게요 ㅎㅎ

문법은 아래 그림과 같습니다.

switch 문법

break문은 뭔가요?ㅎㅎ

break문은 반복문할 때도 한 번 언급했었는데, 반복문일 때에는 break를 만나면 반복문을 빠져나가죠?

switch에서도 마찬가지로 break를 만나면 switch문을 빠져나갑니다.

 

그림에서 보면 switch 괄호 안에 2라는 숫자가 들어왔어요 ㅎㅎ

그럼 case 1:로 갔다가 case 1이랑 일치하지 않으므로 case 2로 내려갑니다.

어라? 2가 일치하네요! 그러면 case 2: 안의 statement2를 수행해요. 그리고 break를 만나서 switch문을 빠져나가는 거랍니다! 그래서 그 밑에 case 3으로는 내려가지 않는거죠 ㅎㅎ

 

만약 break문을 생략하면, case문 사이의 구분이 없어지므로 다른 break를 만나거나 switch 문 블럭{}의 끝을 만날 때까지 모든 문장을 수행합니다.

 

비교해볼까요?

[break문이 있을 때 Java 코드]

import java.util.Scanner;
public class Main {
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		switch (num) {
		case 1:
			System.out.println("step 1"); break;
		case 2:
			System.out.println("step 2"); break;
		case 3:
			System.out.println("step 3"); break; 
		default:  //그 외 라는 뜻
				System.out.println("step 4");
		}
	}
}

[break문이 없을 때 C++ 코드]

#include <iostream>
using namespace std;
int main() {
	int num;
	cin >> num;
	switch (num)
	{
	case 1:
		cout << "step 1\n";
	case 2:
		cout << "step 2\n";
	case 3:
		cout << "step 3\n";
	default:
		cout << "step 4\n";
	}
}

언어 차이일 뿐이지 같은 로직이예요 ㅎㅎ

num에 2를 입력했을 때 양쪽의 결과를 비교해볼께요 ㅎㅎ 

break문이 있을 때에는 case 2만 실행하고 switch문을 빠져나옵니다

반면 break문을 생략해주면 이렇게 case 2를 찾아간 다음 switch의 중괄호인 '}'를 만나기 직전까지의 남은 case들을 다 실행시켜버려요.

고의적 break 생략?- 폴스루 

그러나 고의적으로 break문을 생략하는 경우도 있답니다 ㅎㅎ

switch (level) {
     case 3:
         grantDelete();  //가장 레벨이 높은 3레벨 사용자에게는 삭제, 쓰기, 읽기 권한을 다 부여
     case 2: 
         grantWrite();
     case 1:
         grantRead();
}

로그인한 사용자의 등급을 체크해서, 등급에 맞는 권한을 부여하는 코드인데요, 등급이 가장 높은 회원에게는 가장 많은 권한을, 적을 수록 권한을 차등하는 방법에 break문 생략을 활용했어요 ㅎㅎ

3레벨 사용자는 삭제 쓰기 읽기 모든 권한을 갖고

2레벨 사용자는 쓰기와 읽기 권한만,

1레벨 사용자는 눈팅만 가능하게...ㅎㅎㅎ

이런식으로 활용되기도 합니다. ㅎㅎ

 

이런 고의적으로 생략하여 흘려보내기 하는 형태를 폴스루(fallthrough)라고 합니다. 

그런데 폴스루 방식으로 작성하면 버그가 발생하기 쉬워요. 의도적으로 흘린건지 빠트린건지 알기도 어렵고요.

그래서 C++17부터는 [fallthrough]]속성을 지정해서 의도적으로 폴스루 방식을 작성했다고 컴파일러에게 알려줄 수 있습니다.

case 조건?

참고로 case에 변수나 실수는 올 수 없습니다.

case '1' : //OK
case "YES" : //OK 문자열
case num : // 에러 - 변수는 올 수 없다
case 1.0 : //에러 - 실수 올 수 없습니다.

근데 만약 num이

앞에 finial int num =1 (자바) 또는 const num =1 (C/C++) 이렇게 상수로 선언되어 있다면 가능합니다. 상수는 가능하니까요 ㅎㅎ

 

참고로 switch문을 순서도로 표현하면 아래와 같아요 ㅎㅎ

switch case vs if else

이제 어느정도 switch문과 if문이 정리되셨죠?

조금 더 차이점을 자세히 알아보자면,

▷ 분기문이 많을 경우, if문보다 switch문을 사용하는게 코드 길이도 줄일 수 있고 가독성도 좋은 경우가 대다수입니다.

▷ while문이 모두 for로 변환시킬 수 있었던 것과 다르게 모든 if문을 switch문으로 변환할 수 있는 건 아니예요 ㅎㅎ

▷ 앞에서도 살펴봤지만 break문을 생략했을 때, 다음 case로 이전시킬 수 있는 특징이 switch에는 있습니다. if else로 구현하려면 goto문을 추가로 사용해야하는데 goto문은 지양해야 하는 문법입니다.) 고러므로 이럴 경우 switch를 활용!

▷ 많은 언어에서 switch가 조건 결과값으로 받는 데이터 타입이 한정되어 있어요 ㅎㅎ if문과는 다르게ㅎㅎ case만 봐도 실수가 안되잖아요 ㅎㅎ 반면 if문은 모든 데이터타입을 받기 때문에 복잡한 표현식이 가능하죠

go to문

마지막으로 위에서 살짝 언급된 goto문에 대해서 간략히 눈으로 찍고만 넘어갈게요.

goto는 go to! 말그대로 어디로 가라! 이렇게 위치를 지정해주면 그 위치로 뿅 넘어가는 문법인데요

아주 오래전에는 goto 필요성에 대한 논쟁도 있었지만, 현재는 goto 사용에 부정적으로 결론이 대부분 났죠 ㅎㅎ

저희 교수님께서도 예전에 goto사용하지 말라고 말하고 넘어간게 가물가물 기억이 나는군요 ㅎㅎ

 

goto의 사용을 부정적으로 보는 이유?

goto의 가장 큰 단점은 프로그램의 자연스러운 흐름을 방해한다는 겁니다.

C언어와 절차지향 프로그래밍 언어에서 프로그램의 흐름을 방해하거나 복잡하게 하는 것은 아주 큰 단점이 됩니다. 그만큼 단순한 흐름이 중요한데 굳이 goto문을 써야하는 상황이 존재하지도 않으니..

게다가 goto문을 자주 이용하게 되면 가독성도 해치고 디버깅 하기도 힘들어져요.

고래서 그런가 자바에는 goto라는 문법도 없습니다 ㅎㅎㅎㅎ Java가 C언어로 만들어진, C언어 이후의 언어라는건 아시죠?ㅎㅎ 자바에서는 goto를 아예 없앴어요 ㅎㅎ

 

그러니 뭔지 문법만 보고 끝내도록 할게요

int main(void)
{
   .... //블라블라 코드
   A: //A라는 이름의 레이블을 이렇게 지정해줘요 ㅎㅎ
   ........ //블라블라 코드
       goto A: // 이 문장을 만나면 레이블 A로 이동합니다!
   .......
}

후 오늘은 여기까지입니다 ㅎㅎ

오늘도 공감 눌러주시고 가실구죠?ㅎㅎ 다음에 질 좋은 포스팅으로 또 돌아올게요 

공감, 댓글, 보답광고는 큰 힘이 됩니다 ! :)