C# - 제어문, 반복문, 함수 (작성중)
제어문
if 문
참고) brace : 중괄호
if 뒤 괄호 안의 조건이 true 이면,
그 아래 brace 안의 명령들을 실행한다.
using System;
namespace CSharpStudy
{
class Program{
static void Main(string[] args)
{
int height = 160;
if(height >= 170)
{
Console.WriteLine("키가 170이하입니다.");
}
}
}
}
if - else 문
using System;
namespace CSharpStudy
{
class Program
{
static void Main(string[] args)
{
int score = 73;
string grade;
if(score <=80)
{
grade = "C";
}
if(score > 80 && score <= 90)
{
grade = "B";
}
if(score > 90 && score <= 100 )
{
grade = "A";
}
}
}
}
if 문만을 사용하여 위와 같이 작성하면
score 가 80 이하인 경우, 80 초과 90 이하인 경우,
90 초과 100 이하인 경우를 모두 작성해야한다. 이렇게 하는게 경우에 따라 좋을 수도?
있겠지만, 논리적으로는 똑같지만, 간결하게 작성하기 위해서는 if - else 문을
사용하는 것이 방법이다.
간단한 예시)
if( score <= 80)
{
Console.WriteLine("불합격");
}
else
{
Console.WriteLine("합격");
}
if() 괄호 안의 조건이 아닐 경우
else 뒤의 brace 안의 명령들을 실행한다.
활용 예시)
using System;
namespace CSharpStudy
{
class Program
{
public static void main(string[] args)
{
int score = 73;
string grade;
if( score <= 80)
{
grade = "C";
}
else if (score <= 90)
{
grade = "B";
}
else if (score <= 100)
{
grade = "A";
}
}
}
}
예시를 들 때 else if 를 사용했는데,
else if 는 앞에 나온 if 문의 조건이 거짓일 경우이면서
else if() 괄호 안의 조건이 참일 경우
switch - case
분기하는 조건의 수가 굉장히 많아질 경우, 가독성이 떨어진다. 이런 경우에 switch - case 문을 사용하는 방법이 있다.
switch( x )
{
case 1:
Console.WriteLine("x = 1 입니다.");
break;
case 2:
Console.WriteLine("x = 2 입니다.");
//...
default:
break;
}
switch case 문의 구조는 대략 이러하다.
- x 자리에 올 수 있는 데이터 타입은 정수 또는 문자열 타입만 가능하다.
- case 뒤에 올 수 있는 것은 정수 리터럴, 숫자 리터럴, 상수 이다.
제어문 활용 예시 (가위 바위 보)
using System;
namespace CSharpStudy
{
class Program{
static void Main(string[] args)
{
Random rand = new Random();
int aiChoice = rand.Next(0,3); // 0~2 사이의 랜덤 값
int choice = Convert.ToInt32(Console.ReadLine());
bool win;
switch (choice)
{
case 0:
Console.WriteLine("당신: 가위");
break;
case 1:
Console.WriteLine("당신: 바위");
break;
case 2:
Console.WriteLine("당신: 보");
break;
}
switch (aiChoice)
{
case 0:
Console.WriteLine("나: 가위");
break;
case 1:
Console.WriteLine("나: 바위");
break;
case 2:
Console.WriteLine("나: 보");
break;
}
if (choice == aiChoice)
{
Console.WriteLine("무승부입니다.");
}
else
{
if (choice == 0 && aiChoice == 2 || choice == 1 && aiChoice == 0 || choice == 2 && aiChoice == 1)
{
Console.WriteLine("승리입니다.");
}
else
{
Console.WriteLine("패배입니다.");
}
}
}
}
}
상수와 열거형
상수
참고)
constant : 일정한, 변하지 않는, 상수의
constance : 상수
const int pi = 3.14;
변수 앞에 const 키워드를 지정하여 선언하면, 변수 값을 강제로 변경할 수 없다.
열거형
각 관련이 있는 자료들끼리 집합하여 관리하는 방법.
변수를 계속 생성하다보면 겹치는 변수들이 생길 수 있는데,
이런 점을 방지할 수 있다.
또한, 관련성 있는 변수들끼리는 묶을 필요가 있다.
// 열거형 선언. Direction 은 열거형 그룹의 이름
enum Direction
{
LEFT, // 디폴트값: 0
UP, // 1
RIGHT, // 2
DOWN // 3
}
// 열거형 활용
static void Main(string[] args)
{
Choice playerDirection = Direction.LEFT;
}
활용 예시)
using System;
namespace CSharpStudy
{
class Program{
enum Choice
{
a,
b,
c
}
static void Main(string[] args)
{
int choice = 0;
switch (choice)
{
case (int)Choice.a:
Console.WriteLine("choice equals a");
break;
default:
Console.WriteLine("xx");
break;
}
}
}
}
반복문
while
while문은 do - while 문과 비슷하지만,
한 번도 실행하지 않아도 되는 경우에는 while 문을 사용한다.
(0번 이상 실행)
do - while 문
do - while 문은 while문과 비슷하지만,
한 번 이상은 실행해야 하는 경우에는 do - while 문을 사용한다.
(1번 이상 실행)
for 문
for each 문
배열에 대해서 학습하고 난 뒤에 읽어보기!
break, continue
break
- for문에서의 break
class BreakTest
{
static void Main()
{
for (int i = 1; i <= 100; i++)
{
if (i == 5)
{
break;
}
Console.WriteLine(i);
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
Output:
1
2
3
4
*/
- switch 문에서의 break
switch문에서는 break는 필수적으로 고려해야하는 사항이다.
1을 입력할 경우,case 3: ~ break;
사이에 있는
Console.WriteLine(...)
만 실행한다.
class Switch
{
static void Main()
{
Console.Write("Enter your selection (1, 2, or 3): ");
string s = Console.ReadLine();
int n = Int32.Parse(s);
switch (n)
{
case 1:
Console.WriteLine("Current value is 1");
break;
case 2:
Console.WriteLine("Current value is 2");
break;
case 3:
Console.WriteLine("Current value is 3");
break;
default:
Console.WriteLine("Sorry, invalid selection.");
break;
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
Sample Input: 1
Sample Output:
Enter your selection (1, 2, or 3): 1
Current value is 1
*/
- 중첩 for문(nested for loop)에서 break
class BreakInNestedLoops
{
static void Main(string[] args)
{
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
char[] letters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };
// Outer loop.
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"num = {numbers[i]}");
// Inner loop.
for (int j = 0; j < letters.Length; j++)
{
if (j == i)
{
// Return control to outer loop.
break;
}
Console.Write($" {letters[j]} ");
}
Console.WriteLine();
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
* Output:
num = 0
num = 1
a
num = 2
a b
num = 3
a b c
num = 4
a b c d
num = 5
a b c d e
num = 6
a b c d e f
num = 7
a b c d e f g
num = 8
a b c d e f g h
num = 9
a b c d e f g h i
*/
위의 예시에서 break를 만나면 break를 포함하는 가장 안쪽 for loop 를 벗어나서
한 수준 위의 for loop의 흐름을 다시 이어간다. 안쪽 for loop를 벗어나면
Console.WriteLine();
를 실행하고 이어서 바깥쪽 for loop를 돈다.
- for 문 안에 switch 문이 있는 경우
class BreakFromSwitchInsideLoop
{
static void Main(string[] args)
{
// loop 1 to 3
for (int i = 1; i <= 3; i++)
{
switch(i)
{
case 1:
Console.WriteLine("Current value is 1");
break;
case 2:
Console.WriteLine("Current value is 2");
break;
case 3:
Console.WriteLine("Current value is 3");
break;
default:
Console.WriteLine("This shouldn't happen.");
break;
}
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
* Output:
Current value is 1
Current value is 2
Current value is 3
*/
switch 문의 break는 for loop를 빠져나가기 위한 것이 아니라,
for loop는 계속 도는 중에, 현재의 branch를 빠져나가기 위해 쓰인 것이다.
continue
counter는 1 부터 10까지 카운트를 센다.
(i < 9)
를 만족하면, continue 부터 if 문을 감싸고 있는
가장 안쪽 for문의 뒤쪽 brace 까지의 명령들을 생략한다.
break문 과는 다르게 if 문을 감싸는 가장 안쪽 for문을 아예
끝내고 for문 바깥으로 빠져나오는 것이 아니라, 다음 카운트를 실행한다.
class ContinueTest
{
static void Main()
{
for (int i = 1; i <= 10; i++)
{
if (i < 9)
{
continue;
}
Console.WriteLine(i);
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
Output:
9
10
*/
소수 판별 놀이
참고)
소수 : 1과 자기 자신을 제외한 어떠한 정수라도 나누어 떨어지지 않는 수.
자기 자신보다 작은 수로 나눴을 때 나머지가 0이면, 소수가 아니다.
class Program
{
public static void Main(string[] args)
{
// 소수 정의 : 1과 자기 자신을 제외하고 나누었을 때 나머지가 0이 아니면 소수이다.
for (int n = 2; n < 100; n++)
{
int i;
for (i = 2; i < n; i++)
{
if ((n % i) == 0)
{
break;
}
}
if( i == n)
{
Console.WriteLine($"{n}은 소수이다.");
}
}
}
}
or
class Program
{
public static void Main(string[] args)
{
// 소수 정의 : 1과 자기 자신을 제외하고 나누었을 때 나머지가 0이 아니면 소수이다.
for (int n = 2; n < 100; n++)
{
int i;
for (i = 2; i < n; i++)
{
if ((n % i) != 0)
{
continue;
}
break;
}
if( i == n)
{
Console.WriteLine($"{n}은 소수이다.");
}
}
}
}
자기 자신보다 작은 소수로 나눴을 때 나머지가 0이면, 소수가 아니다.
class Program
{
public static void Main(String[] args)
{
int[] prime = new int[100];
int counter = 0;
prime[counter++] = 2;
for (int n = 3; n < 100; n++)
{
int i = 0;
for(i = 0; i < counter; i++)
{
if ((n % prime[i]) == 0)
{
break;
}
}
if ( i == counter)
{
prime[counter++] = n;
Console.WriteLine($"{n}은 소수입니다.");
}
}
}
}
함수
코드의 크기가 길어질수록 코드를 기능별로 묶어서 사용할 필요가 있다.
사용했던 코드를 재사용 한다는 점에서 생산성 측면에서 유리하다.
메서드, 프로시저라고도 부른다.메서드는 클래스 안에 작성해야한다.
코드를 작성할 때, 함수는 선언하는 부분과 호출하는 부분으로 나눌 수 있다.
실생활에서 함수는 어떤 입력에 따라서 로직을 처리하여 어떤 결과를 출력하는 것으로 알고 있다.
함수의 선언
한정자 반환형식 이름(매개변수목록)
{
// 명령들의 묶음
// 프로그램의 기능별로 묶는 것이 함수를 사용하는 목적이다.
}
(참고) 참조와 복사
class Program
{
static void Sum(int a, int b, int result)
{
result = a + b;
}
public static void Main(string[] args)
{
int result = 0;
Sum(3,4,result);
Console.WriteLine($"result = {result}");
}
}
// Output:
result = 0
위의 예시 코드를 보면 알 수 있듯이,
Sum()함수는 정수 a,b,result를 인자로 받으며, a와 b의 값을 더하여
result에 할당하는 명령을 수행하는 함수이다.
그리고 Main함수에서는 정수 result를 선언했으며, 0으로 초기화했다.
Sum()함수에 3,4,result를 넘겨주었다. result가 7일 것이라고 예상했지만,
result는 여전히 0인 것을 알 수 있다.
인자로 값을 넘길 때, 복사로 넘길 수 있고, 참조로 넘길 수 있다.
기본적으로 위처럼 인자를 넘기는 경우를 복사로 인자를 넘기는 경우이다.
단순히 result 변수의 값을 복사하여 인자로 넘겨주었다.
참조로 넘기는 것은 변수의 값을 복사하여 인자로 넘기는 것이 아니라,
실제로 변수를 참조하여 넘긴다.
class Program
{
static void Sum(int a, int b, int r)
{
r = a + b;
Console.WriteLine($"Method: result = {r}"};
}
public static void Main(string[] args)
{
int result = 0;
Sum(3,4,result);
Console.WriteLine($"Main: result = {result}");
}
}
// Output:
Method: result = 7
Main: result = 0
ref, out
ref
ref는 함수를 선언할때, 변수를 참조해서 넘길 때 붙이는 키워드이다.
즉, 예시에서 Main 함수에서 작성한 result 값에 반영하고 싶을 때 ref키워드를 이용한다.
변수를 참조해서 넘긴다.
class Program
{
static void Sum(int a, int b, ref int r)
{
r = a + b;
Console.WriteLine($"Method: result = {r}"};
}
public static void Main(string[] args)
{
int result = 0;
Sum(3,4,ref result);
Console.WriteLine($"Main: result = {result}");
}
}
// Output:
Method: result = 7
Main: result = 7
ref 키워드를 이용해서 result를 받는것과,
함수의 반환형식을 int로 지정하여 result를 반환받는 것이 비슷해보인다.
(1)함수의 반환형식을 void로 지정하고, ref 키워드를 사용하는 경우
class Program
{
static void Sum(int a, int b, ref int r)
{
r = a + b;
}
public static void Main(string[] args)
{
int result;
Sum(3,4,ref result);
Console.WriteLine($"Main: result = {result}");
}
}
// Output:
Main: result = 7
(2)함수의 반환형식을 int로 지정하는 경우
class Program
{
static int Sum2(int a, int b, int r)
{
r = a + b;
return r;
}
public static void Main(string[] args)
{
int result;
result = Sum2(3,4,result);
Console.WriteLine($"Main: result = {result}");
}
}
// Output:
Main: result = 0
함수의 반환형식을 int로 지정하는 경우(2)가 더 유리하다. 비록 (1),(2)의 논리흐름은 똑같지만, (2)의 경우에는 result의 값을 변경하지 않고, 반환한 값을 다른 곳에 저장해둘 수 있고, 또한 나중에 필요하면 result에 그 값을 할당하여 사용할 수 있다. 예를 들어, 우리가 실제로 result 값을 변경하지 않고, 테스트를 위한 거였으면 result 값이 변경되면 안된다. (1)의 경우는 실제로 result의 값을 변경하고 싶을 때 유리할 수 있다. 또는 함수의 결과로 여러 개의 변수 값을 반환받고 싶을 때는 ref를 사용하여 함수를 정의하고 호출해야한다. 아래와 같은 상황이 그 예이다.
class Program
{
static void Swap(ref int a, ref int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
public static void Main(string[] args)
{
int n1,n2;
n1 = 3; n2 = 7;
Swap(ref a,ref b);
}
}
오버로딩
이름의 재사용
이름만 같고, 반환 형식 or 매개 변수의 종류 또는 개수가 다른 함수를 작성하는 경우를 오버로딩이라고 한다.
우리가 직접 오버로딩해서 사용할 경우도 있지만 공식 API 문서를 볼 때 자주 볼 수 있는 경우이다.
API 문서를 읽을 때, 메서드 여러개가 같은 이름이지만, 반환 형식이 다르거나, 매개변수의 종류 또는 수가 다른 상황이 있다.
예시) Java Integer documentation
댓글남기기