본문 바로가기
개발개발/c++

c++의 조건문, 난수, 사용자 정의 변수타입, enum class, 반복문, 배열

by 유잉유잉유잉 2024. 12. 5.
728x90

🎯 조건문

▶️  if 

- if 조건문은 여러 조건 중 하나를 처리하는 기능 입니다.

- 동작 순서 : 조건식을 체크하여 조건식이 true일 경우 코드 블록안의 코드가 동작합니다.

// if의 구조 
if( 조건식 )
{
	// 해당 조건식에 해당할 경우 동작할 코드
}
//else if문 구조
else if( 조건식 )
{
 	// 해당 조건식에 해당할 경우 동작할 코드
}
//else문 구조
else 
{

}

▶️ else문

  • else는 반드시 if와 함께 사용 되어야 합니다.
  •  if 문의 조건이 false일때 동작
  • if문 다음에 위치해야 한다. else if()문이 있다면 else if()문 다음에 위치해야 합니다.
  • 하나의 if문에 else는 하나만 존재할 수 있습니다.

▶️  else if문

  • 반드시 if와 함께 사용 되어야 합니다.
  • 우선순위 높은 친구가 위에 위치해야 합니다.

 

▶️if문 예시 

int hp = 50;

if ( hp >= 30 && hp <= 50 )
    printf( "print if true. hp is %d\n", hp );
    
else if( hp <= 51 && hp >= 70 )
    printf( "hp is 51 up, and 70 down" );
    
else
    std::cout << "not hp" << std::endl;

 

▶️  switch 조건문 

  • 변수로 분기처리가 가능 합니다.

    switch( 변수 )
    {
    case 상수 :
      동작할코드
      break;
    case 상수 :
      동작할 코드
      break;
    default :
      동작할 코드
      break;
    }

  • 변수의 값에 따라 case의 상수를 찾아서 코드를 동작시키고 break를 만나면 switch문을 나갑니다.
  • case문에서 변수를 사용하려면 코드블럭으로 묶어서 사용합니다.
  • 선언하며 초기화 함께하면 에러.선언하고, 초기화 따로하면 됨. case 사이에 같은 변수 선언(만 한거)는 안됩니다.
  • default : if문의 else와 같습니다. case의 상수들과 같은 값이 없을 경우 default가 동작합니다
int num = 3;
switch (num)
{
case 1 :
	// num이 1일때, 동작할 코드
	break;
case 2 :
	// num이 2일때, 동작할 코드
	break;
case 3:
	// num이 3일때, 동작할 코드
	break;
default :
	// num이 1 또는 2 또는 3이 아닐때(위 케이스들이 한번도 동작하지 않았을때), 동작할 코드
	break;
}

 

 

‼️ switch문 사용시 주의점

1. switch문에선 case문에서 break없이 동작이 가능합니다

int num = 0;
int result = 0;

switch( num )
{
case 0 :
case 1 :
	result = 1;
   	break;
case 2 :
	result = 2;
    break;
default :
	result = -1;
    break;
}

num 변수가 0 또는 1 일때 result엔 1이 대입 됩니다.

 

2. case문 안에서 변수를 선언과 동시에 초기화시 에러가 납니다.

int num = 0;
switch (num)
{
case 0:
    int num2 = 2;
    break;
case 1: // [에러] error C2360: 'num2' 초기화가 'case' 레이블에 의해 생략되었습니다.
    break;
default : // [에러] error C2361: 'num2' 초기화가 'default' 레이블에 의해 생략되었습니다.
    break; 
}

밑에 case, default문에서도 해당 변수를 사용(위 예시에서는 num2)할 수 있는데, 중복으로 선언되어 사용할 수 있기때문에 에러를 발생시키게 됩니다.

 

해결방법 1. 코드블럭으로 묶어주기

int num = 0;
switch (num)
{
case 0:
{
	int num2 = 2;
}
	break;
case 1:
	break;
default :
	break;
}

 

해결방법 2. 선언과 초기화를 나눠서 하기 

int num = 0;
switch (num)
{
case 0:
	int num2;
	num2= 2;
	break;
case 1:
	// 이렇게 위 케이스에서 선언한 변수를 사용 가능합니다.
	num2 = 4;
	break;
default :
	break;
}

위 예시에서 case1에 num2를 다시 선언한다면 에러가 나게 됩니다.

 

case안에서 변수 선언 후 다른 case에서 사용은

문법적으론 가능한 내용이나, 가독성이 좋지않아서 저런 사용은 지양하시길 바랍니다.

 

 


 

🎯 난수 

▶️ rand() 

- 난수를 발생시켜 주는 함수 입니다

- 그냥 실행하면 동일한 숫자가 계속 나옵니다. 이미 정해진 난수표를 가지고 있기 때문입니다.

std::cout << rand() << std::endl; // 출력 : 41
std::cout << rand() << std::endl; // 출력 : 18467

 

 

▶️ srand( n ) : n번째 시드에 있는 난수표 값을 가져옵니다

srand(20);
std::cout << rand() << std::endl; // 103
std::cout << rand() << std::endl; // 26079

 

▶️ srand( time(0) ) : 매번 출력할 때마다 다른 값을 넣어주려면 현재 시간( time(0) )을 넣어주면 됩니다.

#include <time.h>

int main()
{
	srand( time(0) );
	std::cout << rand() << std::endl; // 이렇게 하면 실행할 때마다 첫번째값이 조금씩만 증가함. c++ 버그임
	std::cout << rand() << std::endl;

	return 0;
}

위 코드를 반복 실행하면 첫번째 값이 비슷한 값에서 조금씩 증가하는것을 확인할 수 있습니다.

이것은 c++버그라고 하네요 😂.

 

위 버그를 방지하기 위해 rand()를 사용하기 전 미리 rand()를 호출해줍니다.

#include <time.h>

int main()
{
	srand( time(0) );
    // 버그를 방지하기 위해 미리 rand()를 호출해줌
	rand();
    
	std::cout << rand() << std::endl;
	std::cout << rand() << std::endl;

	return 0;
}

 

 

▶️  rand()의 발생 범위를 0 - 100까지만 난수로 사용하고 싶을때

std::cout << rand() % 101 << std::endl;

 

 

▶️ 100 - 200 사이의 값만 난수로 사용하고 싶을 때 

std::cout << rand() % 101 + 100 << std::endl;

 

특정 범위의 최소 ~ 최대 난수를 구하는 공식은

rand() % (최대 - 최소 + 1) + 최소 임을 알 수 있습니다.

 

 

▶️ 난수를 실수로 표현하기

소숫점 둘째자리까지 난수를 만들고 싶은 경우

0 ~ 10000 사이로 난수가 나오게 하고싶을 때 

std::cout << rand() % 10001 * 0.01f << std::endl;

 


 

 

🎯 사용자 정의 변수타입 

 

  • 해당 프로젝트만의 고유한 변수 타입을 만들어서 사용 가능합니다.
    ex) 열거형, 구조체, 클래스 

▶️ 열거형(enum) : 사용자 정의 변수타입을 만들어 주기 위한 하나의 문법.

  • 열거형은 연속된 숫자 상수를 만들어 줄 수 있는 기능입니다.
  • 숫자에 이름을 부여해 줄 수 있습니다.
  • 열거형은 기본으로 4바이트의 크기를 가지게 됩니다.
  • 크기 지정도 가능합니다.
#include <iostream>

// enum은 변수명에 대문자 E가 붙어있음.
// enum이 다르더라도 열거자(변수)가 같으면 안됨 (그래서 나온게 enum클래스)
enum EJob
{
	Job_None,
	Job_Knight,
	Job_Archer,
	Job_Magicion,
};

enum ENumber
{
	Num1, // 0
	Num2, // 1
	Num3 = 50,
	Num4, // 51
};

enum EBuf
{
	Buf_Attack	= 0x1,
	Buf_Defence = 0x2,
	Buf_Hp		= 0x4,
	Buf_Mp		= 0x8,
	Buf_Exp		= 0x10,
};

int main()
{
    std::cout << Job_Knight << std::endl;		// 1출력
    std::cout << EJob::Job_Archer << std::endl;	// 2 출력

    EJob job;
    // sizeof() : 괄호안에 들어가는 타입 혹은 변수의 메모리 크기가 몇 바이트인지 구해준다
    std::cout << sizeof(EJob) << std::endl; // 4 출력		// 4바이트
    std::cout << sizeof(int) << std::endl;	// 4출력		// 4바이트 
	return 0;
}

 

열거형의 크기지정

enum ETest : unsigned char
{
    a,
    b,
    c
};

 

 

열거형 변수에 정수를 직접 할당 할순 없습니다.

형변환을 거쳐야합니다.

EJob job;

job = 0; // [에러] "int" 형식의 값을 "Test" 형식의 엔터티에 할당할 수 없습니다.

job = (EJob)0; // 형변환을 해주면 대입이 가능합니다.

 

 

반대로 정수형 변수에 열거형 변수는 대입이 가능합니다.

EJob job = EJob::None;

int num = job;

std::cout << num << std::endl; // 0 출력

 

 

 

enum과 switch를 이용한 예제

// switch 만들때, tab으로 만들고, 인수에 Enum형 치고 탭 누르고 엔터 누르면 열거형 별로 케이스 자동완성
switch ( jobInt )
{
    case Job_None:
        break;
    case Job_Knight:
        break;
    case Job_Archer:
        break;
    case Job_Magicion:
        break;
}

-> 대부분 enum과 switch를 함께 사용합니다. 가독성 상승하여 읽기 좋은 코드가 됩니다.

 

▶️  enum class 

  • enum class는 타입을 엄격하게 따집니다.
  • 기본 4바이트입니다.
     
  • 언리얼에서는 enum class로 구현되어있는 경우가 많아서 훨씬 많이 사용합니다.
  • 크기 지정이 가능합니다.

  • enum class는 인트 변수에 넣을 수가 없습니다.
    • int itemType = EItemType::Weapon; // [사용 불가]
      std::cout << EItemType::Weapon << std::endl;
    • EItemType itemType = EItemType::Weapon;
    • itemType = Armor; // [사용불가] 열거형 이름의 요소 이름만으로는 접근이 안됩니다. 이름 충돌이 될 가능성이 적어져서 안전함이 장점입니다.
#include <iostream>

// enum 클래스는 크기를 지정 가능. enum class의 장점
// enum clase
enum class EItemType : unsigned char
{
	Weapon,
	Armor,
	Etc,
};

int main()
{
    // enum class는 인트 변수에 넣을 수가 없음.
    //int itemType = EItemType::Weapon; // [에러] "EItemType" 형식의 값을 사용하여 "int" 형식의 엔터티를 초기화할 수 없습니다.
    //std::cout << EItemType::Weapon << std::endl; // [에러]
    EItemType itemType = EItemType::Weapon;
    //itemType = Armor; // 열거형 이름의 요소 이름만으로는 접근이 안됨. 이름 충돌이 될 가능성이 적어져서 안전함이 장점.
	
    return 0;
}

 

 

 


 

 

🎯 반복문

  • 동일한 작업을 여러번 반복해서 동작시킬 때 사용합니다.
    for, while, do while 3가지 반복문을 지원합니다.

 

▶️  for문

for( 초기값 ; 조건식 ; 증감값 )
{
   반복할 코드 
}
=> 초기값과 증감값은 없어도 됩니다.

  • - 동작순서
    초기값 -> 조건식 체크 true -> 코드동작 -> 증감값 -> 조건식 true -> 코드동작 -> 증감값 
    -> 조건식 false일 경우 종료

  • 초기값은 처음 for문에 진입할 때 1번만 동작 합니다.

    1. 조건식 체크
    2. 코드동작
    3. 증감값
    이 순서대로 반복한다.

  • 조건식이 false가 될 수 없는 조건이라면 무한루프가 돌게 됩니다.
    이 경우 break를 이용해 for문을 빠져나올 수 있습니다.

 

// 처음 아래 for문에 진입할 때 초기값에서 int 변수 i를 생성하고
// 0으로 초기화한다. i는 초기값에서 생선된 변수이기 때문에
// for문 안에서만 사용이 가능하다.
// 조건식을 체크한다. i < 10은 처음에는 i가 0이므로 0<10 이 되고
// 0은 10보다 작기 때문에 true가 되어 for문 안으로 진입한다.
// i값을 출력해서 0이 출력되고, 증감값으로 이동한다.
// 증감값에서는 ++i를 해주고 있다. 그러므로 i는 0에서 1이 되고
// 조건식을 체크한다 
for (int i = 0; i < 100; ++i)
{
	// continue는 증감값으로 실행흐름을 변경해준다.
	// 0 % 3 == 0이 나옴. 0도 가능해야 한다면 조건 추가 ( i != 0 && i % 3 == 0 )
	if (i % 3 == 0)
		continue;

	std::cout << i << std::endl;
}

 

for문으로 무한루프 돌리기

for (;;)
{

}

 

중첩 for문 사용하기

// 중첩for문
for (int i = 0; i < 3; ++i)
{
	for (int j = 0; j < 3; ++j)
	{
		//아래와 동일하게 printf로 출력하기 : printf("i : %d, j : %d", i, j);
		std::cout << "i = " << i << ", j = " << j << std::endl;
	}
}

/*
i : 0, j : 0
i : 0, j : 1
i : 0, j : 2
i : 1, j : 0
i : 1, j : 1
i : 1, j : 2
i : 2, j : 0
i : 2, j : 1
i : 2, j : 2
*/

 

0~49까지 이중 for문을 통해 출력하기 예시 

int num1 = 5;
int num2 = 10;

for( int i = 0 ; i < num1 ; ++i)
{
	for( int j = 0 ; j < num2 ; ++j)
    {
    	std::cout << i * 10 + j << std::endl;
    }
}

세로 줄번호 * 가로갯수 + 가로 번호 라는 식을 세워서 작성할 수 있습니다.

많이 사용되는 인덱스 계산 공식이기 때문에 외워두시길 추천드립니다.

 

 


▶️   while문

while( 조건식 )
{
  동작할 코드
}

  • 조건식이 true면 반복 합니다.


▶️    do while문

do
{
}
while( 조건식 ) ;

  • 처음 한번은 무조건 실행합니다.
  • 한번 실행 후 조건을 체크합니다.
  • 멀티쓰레드 환경에서 사용하기 좋음: 락카운트할때! 한번은 락카운트 한번 해줘야하기 때문입니다.

 

런타임 : 프로그램 실행되고 있는 상태를 말합니다.

 

▶️ 콘솔에서 입력받기

  • std::cin : 콘솔창에서 입력을 받고, 그것을 변수에 넣어서 사용할 수 있게 해주는 함수
  • 화살표를 cout의 반대로 넣어줘야 합니다.
  • 정수가 아닌 string등이 입력될 경우 입력오류가 납니다.
int loopCnt = 0;
std::cout << "input loopCount : ";
std::cin >> loopCnt;

for ( int i = 0; i < loopCnt ; ++i )
{
	std::cout << i << std::endl; // 입력받은 갯수부터 0까지 숫자 감소하며 출력
}

 

 

 


 

 

▶️ 배열 

  • 동일한 타입의 변수 여러개를 하나의 이름으로 만들어서 사용할 수 있게 해주는 기능입니다
  • 선언 형태 : 변수타입 이름[개수];

  • 배열은 인덱스를 이용해서 접근합니다 : 배열이름[접근할 인덱스 번호]
  • 인덱스는 0 ~ 개수-1 까지 입니다.
    ex) int arr[4] -> 0, 1, 2, 3 

  • 배열 선언시 = {}를 해주게 되면 배열에 모든 요소를 0으로 초기화 시킵니다.
    ex) int arr[4] = {1,2} 해주게 되면 1, 2, 0, 0 으로 초기화 됩니다.
const int size = 10;
// 인덱스 0번 배열에 1, 인덱스 1번 배열에 2를 넣고 나머지 배열엔 0을 대입 
// 배열 사이즈에는 상수만 입력 가능
int Array[size] = { 1,2 };

// 직접 접근하여 대입 가능 
Array[3] = 500; 

// 9번째 배열 값에 전치증가연산자 사용
++Array[9];

for (int i = 0; i < size; ++i)
{
	std::cout << Array[i] << std::endl;
}

 

 

‼️ 배열 사용시 주의사항

배열 선언시 초기화를 해주지않거나, 값을 대입하지 않고 사용할 경우 쓰레기값이 들어가게 됩니다

const int size = 3;

int arr[size];

for (int i = 0; i < size; i++)
{
	std::cout << arr[i] << std::endl;
}

 

 

 

 

아래 예제에선 char형태의 배열에서 초기화도 하지않고, null문자도 지정해주지 않아서

입력된 문자 외에 쓰레기 값이 저장되어 출력되는것을 확인하실 수 있습니다. 

char chars[32];

chars[0] = 'a';
chars[1] = 'b';

std::cout << chars << std::endl;

 

컴파일러가 chars를 출력하기 위해 chars를 읽어올 때,

null 문자까지 가져와야 하나 null문자가 없어서 위와 같이 결과가 출력됩니다. 

 

// 선언과 동시에 초기화 
char chars[32] = {};

chars[0] = 'a';
chars[1] = 'b';

std::cout << chars << std::endl;

chars 배열에 선언과 동시에 초기화를 함으로써 우리가 원하는 값만 출력되도록 변경되었습니다.

 

 

728x90

댓글