[C/C++] 12. 함수(1) / 반복문
C/C++ 실행 구조의 핵심인 메인 함수의 역할과, 프로그램이 지속해서 동작할 수 있도록 만들어주는 반복문(Loop)의 개념 및 사용법에 대해 알아본다. C/C++에서 함수는 프로그램의 진입점(Entry Point)이자 시작점이다. - 프로그램 실행 흐름 1. 프로그램이 시작되면 가장 먼저 함수가 호출된다. 2. 함수 내부에서 필요에 따라 다른 사용자 정의 함수(예: 등)를 호출하여 처리 결과를 받아온다. 3. 함수의 모든 코드가 실행을 마치고 반환()되면 프로그램 전체가 함께 종료된다. > ❓ 일반 프로그램은 왜 바로 종료되지 않고 계속 실행될까? > > 게임이나 일반 응용 프로그램은 사용자가 직접 끄기 전까지 계속 실행된다. 이는 함수 내부 전체를 감싸고 있는 거대한 반복문(메인 루프)이 존재하여 지속적으로 코드를 유지하기 때문이다. 특정 조건이 참(true)인 동안 코드 블록을 반복적으로 수행할 때 사용하며, 대표적으로 문과 문이 있다. 반복 횟수가 비교적 명확할 때 자주 사용하며, 초기화식, 조건식, 증감식이 한 줄에 작성되어 구조가 깔끔하다. - 동작 순서: 초기화 (최초 1회) $\rightarrow$ 조건 체크 $\rightarrow$ 코드 수행 $\rightarrow$ 증감식 수행 $\rightarrow$ 조건 체크 반복 - 문 내부에서 선언된 변수 는 해당 문 안에서만 유효한 지역 변수다. 조건식 중심의 반복문으로, 반복자 관리(초기화 및 증감)는 프로그래머가 코드 내부에서 직접 처리해 주어야 한다. 반복문 수행 도중 흐름을 변경해야 할 때 사용하는 특수 키워드다. - * - 현재 회차의 남은 아래 코드를 실행하지 않고 건너뛰어 다음 반복 회차(증감식 단계)로 넘어간다. - 예: 특정 홀수/짝수 회차만 건너뛰고 출력을 계속하고 싶을 때 사용 - * - 조건 성립 여부와 상관없이 반복문을 즉시 탈출(강제 종료)한다. - 예: 사용자의 특정 입력이나 종료 명령어(Alt+F4 등)를 감지하여 프로그램 메인 루프를 끝낼 때 사용 - 메인 함수는 프로그램의 진입점이자 시작점으로 이 함수가 종료되면 프로그램도 함께 종료된다. - 반복문인 for문과 while문을 활용하면 조건이 완료될 때까지 특정 코드 블록을 반복적으로 실행할 수 있다. - 반복문 내부에서 continue를 사용하면 현재 회차를 건너뛰고, break를 사용하면 반복문 자체를 즉시 강제 종료할 수 있다.
[C/C++] 11. 변수
프로그래밍을 작성할 때 변수를 어디에 선언하느냐에 따라 변수의 수명과 접근할 수 있는 범위가 완전히 달라진다. 이번 시간에는 지역 변수와 전역 변수의 개념, 함수와 모듈화의 중요성, 그리고 중첩 블록에서의 변수 우선순위에 대해 알아본다. 변수를 나누는 가장 명확한 기준은 바로 '함수() 내부인가, 외부인가'이다. - 지역 변수 (Local Variable) - 함수 내부나 특정 중첩 블록() 안에서 선언된 변수다. - 선언된 해당 함수 또는 블록 내부에서만 유효하며, 블록을 벗어나면 메모리에서 소멸하여 접근할 수 없다. - 지금까지 작성했던 메인 함수 내부의 변수들이나, 함수의 매개변수(인자)들이 모두 지역 변수에 해당한다. - 전역 변수 (Global Variable) - 특정 함수 외부에 선언된 변수다. - 프로그램 전체 어디서나 접근 및 수정이 가능하며, 프로그램이 종료될 때까지 메모리에 유지된다. 프로그래밍에서 함수는 단순한 코드의 집합이 아닌, 독립적인 minimum 단위의 기능을 의미한다. - 기능의 분리 - 거대한 시스템(예: 게임 캐릭터 커스터마이징, 전투 시스템 등)도 결국 잘게 쪼개진 작은 기능들의 조합으로 이루어진다. - 재사용성과 확장성 - 하나의 함수에 너무 많은 기능을 집어넣으면 다른 곳에서 해당 기능을 재가공하여 쓰기 어렵다. - 바닥부터 작은 단위의 기능을 명확히 쪼개어(모듈화) 구현해야 필요할 때 해당 모듈을 가져다 재사용할 수 있다. C/C++에서는 함수 내부에서도 중괄호()를 사용해 새로운 지역(중첩 블록)을 생성할 수 있다. - 동일 지역 변수명 중복 불가 - 같은 지역 안에서는 이름이 같은 변수를 중복 선언할 수 없다 (컴파일 에러 발생). - 중첩 블록에서의 변수 가리기 (Variable Shadowing) - 외부 지역에 존재하더라도, 내부 지역에서 동일한 이름으로 변수를 다시 선언하면 내부 지역에서는 해당 이름으로 선언된 변수가 우선순위를 가진다. - 내부 블록이 끝나는 순간, 그 안에서 선언되었던 지역 변수는 소멸하며 다시 바깥쪽 지역 변수를 참조하게 된다. - 변수는 선언된 위치에 따라 함수 내부의 지역 변수와 외부의 전역 변수로 구분되며, 이들은 함수라는 기준을 통해 명확하게 나뉜다. - 프로그래밍에서의 함수는 특정 기능을 최소 단위로 모듈화하여 관리하는 것이며, 잘게 쪼개진 모듈을 효율적으로 조합할수록 복잡한 기능을 체계적으로 구현할 수 있다. - 동일한 지역 내에서는 변수명의 중복이 불가능하나, 지역 안에 새로운 지역을 선언할 경우 내부 지역의 변수가 우선순위를 가지며 외부 변수와의 모호성을 해결한다.
[C/C++] 10. 비트 연산자(2)
지난 시간에는 비트 시프트 연산과 비트 논리 연산자, 그리고 매크로의 기초 개념을 다루었다. 이번 시간에는 비트 연산자가 실무 및 게임 프로그래밍에서 실제로 어떻게 활용되는지 핵심적인 사용처인 상태 플래그(State Flag) 관리에 대해 알아본다. 32비트 정수형 변수() 하나는 총 32개의 0 또는 1(Bit)을 가질 수 있다. 이를 이용하면 변수 하나로 최대 32가지의 서로 다른 상태를 동시에 관리할 수 있다. - 비트 마스킹의 원리 - 각 비트 자리에 고유한 의미(예: 배고픔, 목마름, 중독 등)를 부여한다. - 해당 비트가 이면 상태 활성화(ON), 이면 비활성화(OFF)를 나타낸다. - 각 상태의 값은 2의 거듭제곱($1, 2, 4, 8, 16 \dots$) 형태를 띤다. > 💡 16진수(0x) 표기법 활용 > > 상태 값을 정의할 때는 10진수보다 16진수() 를 주로 사용한다. 16진수는 비트 4개 단위로 깔끔하게 떨어지기 때문에 각 비트의 위치를 직관적으로 파악하고 관리하기 쉽다. 상태 플래그를 관리할 때 자주 쓰이는 추가, 검사, 제거의 3가지 연산 패턴은 반드시 숙지해야 한다. 특정 비트 자리를 로 만들어 상태를 온(ON)시킨다. 다른 비트 자리에는 영향을 주지 않는다. 특정 상태가 활성화되어 있는지 확인한다. 해당 비트 자리가 이면 이 아닌 참(True)의 값을 반환한다. 지우고자 하는 상태 비트만 으로 뒤집은 뒤() AND 연산을 수행하여 안전하게 상태를 제거한다. > ⚠️ XOR() 연산으로 상태를 제거하면 안 되는 이유 > > XOR 연산은 비트가 다르면 , 같으면 을 반환하므로 이미 해당 상태가 켜져 있을 땐 꺼지지만, 상태가 꺼져 있을 때 적용하면 오히려 상태가 켜져 버리는 문제가 발생한다. 따라서 상태 제거 시에는 안전하게 패턴을 사용한다. - 게임 캐릭터의 버프/디버프 관리 - 캐릭터가 현재 어떤 상태(비행, 무적, 독, 둔화 등)에 걸려 있는지 32비트 또는 64비트 변수 하나로 집약하여 빠르게 처리한다. - 복잡한 설정을 하나의 인자로 전달 - 윈도우창 스타일 설정(최소화 버튼 유무, 닫기 버튼 유무, 창 크기 조절 여부 등)처럼 수많은 설정값을 하나의 변수에 묶어서 함수에 전달할 수 있다. - 32비트 정수형 변수의 각 비트 자리를 개별적인 상태 값으로 활용하여 최대 32가지의 상태를 동시에 효율적으로 관리할 수 있다. - 비트 OR 연산으로 특정 상태를 추가하고, 비트 AND와 반전 연산을 조합하여 특정 상태를 안전하게 제거하는 패턴은 게임 개발에서 빈번하게 사용된다. - 복잡한 매개변수를 하나로 통합 전달하거나 윈도우 스타일 설정 등 여러 상태 값을 하나의 변수에 담아 효율적인 시스템 설계가 가능하다.
[C/C++] 9. define / 비트 연산자(1)
C/C++을 공부하다 보면 처음엔 다소 생소하지만, 게임 프로그래밍 및 실무에서 정말 자주 쓰이는 핵심 개념 두 가지를 만나게 된다. 바로 비트 연산자와 * 전처리기다. 자주 사용되지만 시간이 지나면 동작 방식을 잊어버리기 쉬운 개념인 만큼, 기본 원리부터 차근차근 정리해 본다. 비트 시프트 연산은 데이터의 비트(Bit)를 화살표가 가리키는 방향으로 한 칸씩 밀어주는 연산자다. - *왼쪽 시프트 ()* - 비트를 왼쪽으로 $N$칸 이동시킨다. - 2진법 특성상 자릿수가 하나씩 올라가므로, *$2^N$배 증가하는 효과를 가진다. - 예: 은 $x \times 2^3$ ($x \times 8$)과 동일 - 대입 연산자와 결합하면 변수 값에 2의 배수를 곱해 바로 재대입할 수 있다. - **오른쪽 시프트 () - 비트를 오른쪽으로 $N$칸 이동시킨다. - 오른쪽으로 밀리면서 범위를 벗어난 비트는 소실(삭제)된다. - 수학적으로는 $2^N$으로 나눈 몫을 구하는 원리와 같다. > 💡 한 줄 요약 > > 왼쪽 시프트는 $2^N$을 곱한 값, 오른쪽 시프트는 $2^N$으로 나눈 몫을 반환한다. 각 자릿수별로 비트 단위 연산을 수행한다. |연산자|이름|설명|특징| |---|---|---|---| ||**AND** (비트 곱)|두 비트가 모두 1일 때만 **1**|특정 비트를 추출하거나 가릴 때 사용| ||OR (비트 합)|두 비트 중 하나라도 1이면 1|특정 비트를 켜거나 추가할 때 사용| ||**XOR** (배타적 논리합)|두 비트의 값이 **다르면 1, 같으면 0**|비트 반전 및 상태 체크에 유용| ||NOT (비트 반전)|0은 1로, 1은 0으로 뒤집음|비트 전체 반전| 비트 연산자가 실제 코드에서 어떻게 유용하게 활용되는지 이해하기 위해, 먼저 전처리기 을 알아본다. 컴파일러가 코드를 실제 기계어로 번역하기 전, 가장 먼저 수행되는 처리 과정이다. 기호로 시작하는 명령어가 이에 해당한다. 은 코드 내 지정한 구문(심볼)을 특정 값(숫자, 문자열 등)으로 단순 치환(Replace)해 준다. 1. 코드의 가독성 향상 - 단순 숫자 대신 처럼 의미가 명확한 단어를 사용하여 코드를 읽고 이해하기 쉽게 만든다. 2. 유지보수 효율 극대화 - 설정값이나 상수가 변경될 때, 코드 전체를 수정할 필요 없이 으로 정의된 부분만 수정하면 일괄 적용된다. - 예: 자판기의 최대 음료 수량()이 변경될 경우, 상단의 설정값 하나만 변경 - 비트 시프트 연산은 $2^N$의 곱셈과 나눗셈 몫을 빠르게 처리하는 수학적 원리를 갖는다. - 비트 논리 연산자(AND, OR, XOR, NOT)의 자릿수별 처리 방식을 파악하여 상태 체크 등에 활용하자. - 매크로로 의미 있는 이름을 부여하여 코드 가독성과 유지보수성을 극대화하자.
[C/C++] 8. switch / case 문과 삼항 연산자
앞서 조건문과 비교 연산자를 알아보았다. 이번 글에서는 특정 변수의 값에 따라 깔끔하게 분기 처리하는 * 문, 핵심 키워드인 *, 그리고 구문을 한 줄로 축약할 수 있는 **삼항 연산자를 정리한다. 문은 기준이 되는 변수의 값과 일치하는 위치로 이동하여 코드를 실행하는 분기문이다. - *: 평가 대상 변수의 값과 일치하는 지점부터 실행한다. - *: 일치하는 가 하나도 없을 때 실행된다. (생략 가능) |구분|if - else if|switch - case| |---|---|---| |조건식|범위 지정 및 복잡한 논리식 가능 ()|정수/문자 형태의 단일 변수 값 비교 위주| |가독성|조건이 많아질수록 코드가 길어지고 복잡해짐|값이 딱 떨어지는 여러 조건 처리 시 가독성이 뛰어남| 가령 게임에서 상태 값(0: 대기, 1: 이동, 2: 공격, 3: 사망)에 따라 연동되는 로직을 구현할 때는 문을 사용하는 것이 훨씬 직관적이다. 문 내에서 를 만나면 해당 블록()을 완전히 탈출한다. 만약 내부에 를 작성하지 않으면, 문법 에러는 나지 않지만 다음 의 코드까지 연속해서 실행되어 최종 까지 도달하는 치명적인 논리 버그가 발생한다. 여러 에 대해 동일한 로직을 수행해야 할 때는 의도적으로 를 생략하여 로직을 하나로 묶을 수 있다. 이는 문에서 논리합() 연산자를 사용하는 것과 같다. 삼항 연산자는 간단한 구문을 단 한 줄로 축약할 수 있는 연산자다. - 삼항 연산자는 코드를 간결하게 만들어 주지만, 조건이 중첩되거나 길어지면 오히려 코드 가독성이 크게 떨어진다. - 따라서 단순히 값을 대입하거나 반환하는 직관적이고 명확한 상황에서만 선택적으로 사용하는 것이 좋다. - 값이 깔끔하게 떨어지는 다중 분기에는 보다 가 가독성 측면에서 유리하다. - 문 작성 시 * 누락으로 인한 논리 오류를 항상 경계하자. - *삼항 연산자**는 간단한 대입식에서만 한정적으로 사용하여 코드 깔끔함과 가독성을 모두 챙기자.