함수
- 함수의 시작 : 받은 매개변수의 초기화부터 시작한다.
스코프
- 중괄호(코드 블럭)
- 스코프 내에서는 같은 이름의 변수가 존재하면 안된다.
- 스코프마다 이름이 같은 변수가 있어도 다른 변수이다.
반환값
- 함수가 작업을 완료한 후 호출한 곳으로 돌려주는 값
- return 키워드를 사용하여 반환
- 함수의 반환 타입과 일치해야 함
반환값이 있는 함수 예시
#include <stdio.h>
int add(int a, int b) {
int result = a + b;
return result; // 계산 결과를 반환
}
double divide(int a, int b) {
if (b == 0) {
printf("0으로 나눌 수 없습니다.\\\\n");
return 0.0; // 오류 상황에서의 반환
}
return (double)a / b; // 나눗셈 결과를 반환
}
int main() {
int sum = add(5, 3);
printf("5 + 3 = %d\\\\n", sum);
double quotient = divide(10, 3);
printf("10 / 3 = %.2f\\\\n", quotient);
return 0;
}
반환값이 없는 함수 (void)
#include <stdio.h>
void printMessage() {
printf("안녕하세요!\\\\n");
// return 문이 없어도 함수 끝에서 자동으로 반환
}
void printNumber(int num) {
printf("입력받은 숫자: %d\\\\n", num);
return; // void 함수에서 명시적으로 반환 (선택사항)
}
int main() {
printMessage();
printNumber(42);
return 0;
}
반환의 특징
- return 문 실행 시 함수 즉시 종료
- 하나의 함수에서 여러 개의 return 문 가능
- void 함수는 반환값 없음
- void 형도 return을 쓰면 함수가 종료된다.
- 반환 타입과 실제 반환값의 타입 일치 필요
Call-by-value, Call-by-address, Call-by-reference
- 보통은 매개변수로 받더라도 다른 스코프에 있는 변수는 접근이 안되지만, ‘포인터’를 매개변수로 받는다면 다른 스코프에 있는 변수라도 접근하여 변경이 가능하다.
- 함수를 사용하여 외부에서 값 변경이 가능해진다.
- C언어에서는 주로 Call-by-value와 Call-by-address를 사용 - 값형식
- C++에서는 Call-by-reference도 지원 - 참조 형식
Call-by-value (값에 의한 호출)
void swapValue(int a, int b)
{
int temp = a;
a = b;
b = temp;
printf("함수 내부: a = %d, b = %d\\\\n", a, b);
}
- 매개변수의 변수값은 매개변수로 들어온 값으로 ‘초기화’되는 것이다.
- 함수에 들어온 값은 함수 종료시 삭제된다.
- 변수가 넘어가는 것이 아니라 변수에 저장된 값이 넘어가는 것이다.
- 넘어간 값의 변경은 원본의 값과 관계가 없다.
Call-by-address (주소에 의한 호출)
void swapAddress(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
printf("함수 내부: *a = %d, *b = %d\\\\n", *a, *b);
}
- 메모리에 접근하여 변경하는 것이기에 바뀐값이 유지된다.
전달 방식 비교
구분 Call-by-value Call-by-address
| 전달 내용 | 변수의 값 | 변수의 주소 |
| 원본 변경 | 불가능 | 가능 |
| 메모리 사용 | 복사본 생성 | 주소만 전달 |
| 안전성 | 높음 (원본 보호) | 낮음 (원본 변경 가능) |
| 사용 시기 | 값만 필요한 경우 | 원본 수정이 필요한 경우 |
프로토 타입
- 함수의 선언부만 미리 작성
- 프로토 타입 등 다 같은 말이다.
- 컴파일러에게 함수의 존재를 알려줌
- 함수의 반환 타입, 이름, 매개변수 정보 포함
- 헤더 파일에 주로 선언된다.
- 물론, 소스코드에서도 많이 사용된다.
- 앞으로 가급적이면 프로토 타입으로 함수를 작성해라
- 헤더 파일 : 선언문, 소스코드 : 구현부
// 함수 프로토타입 선언
int add(int a, int b);
void printResult(int result);
int main() {
int sum = add(5, 3); // 프로토타입이 있어 컴파일 가능
printResult(sum);
return 0;
}
// 함수 정의는 main 함수 뒤에 위치
int add(int a, int b) {
return a + b;
}
void printResult(int result) {
printf("결과: %d\\\\n", result);
}
- 함수의 정의가 없고, 선언만 되어있다면? : 컴파일 오류가 생긴다.
- 이름, 매개변수 데이터형, 반환하는 데이터형(반환값을 받는 메인함수의 변수)가 필요하다.
- 반환값이 없다면 마지막거는 상관없다.
프로토타입 작성 규칙
// 매개변수 이름 포함
int add(int a, int b);
// 매개변수 이름 생략
int add(int, int);
// 둘 다 유효한 프로토타입
- 매개변수 이름은 생략 가능 (타입만 명시)
- 헤더 파일에 주로 작성
- 세미콜론(;)으로 종료
배열을 매개변수로 넘기기
- 배열은 항상 주소로 전달됨 (Call-by-address)
- 시작 주소를 넘기는 것이다.
- 배열의 크기 정보는 별도로 전달 필요
- 함수 내에서 배열 원소 수정 시 원본 배열도 변경됨
void printArr(int parr[4]) {
for (int i = 0; i < 4; i++) {
printf("%d ", parr[i]);
}
printf("\\\\n");
}
int main() {
int arr[4] = { 1, 2, 3, 4 };
printArr(arr);
return 0;
}
- int parr[4] ⇒ int* parr로 넘기는것이 좋다.
- 사이즈가 안적혀있는데 사이즈를 같이 넘기는것이 좋다.
- 반드시 사이즈도 넘겨라
- sizeof(arr) / sizeof(int) ⇒ 배열의 갯수 확인 ⇒ 배열 크기를 함께 전달하는 방법
배열 매개변수의 다양한 표현
// 모두 동일한 의미
void printArr(int parr[4]); // 크기 명시
void printArr(int parr[]); // 크기 생략
void printArr(int* parr); // 포인터로 표현
이차원 배열 매개변수
- 첫 번째 차원 크기는 생략 가능
- 두 번째 차원 크기는 반드시 명시 필요
- 메모리 레이아웃을 알기 위해 열 크기 정보 필수
// 올바른 선언
void func(int arr[][4], int rows); // 열 크기 명시
void func(int arr[3][4]); // 행, 열 크기 모두 명시
// 잘못된 선언
void func(int arr[][], int rows); // 열 크기 생략 불가
- int arr[][4] == int (*arr)[4]와 같은 의미다.
- int* ptr, int rows, int cols로 매개변수를 받을 수도 있다.
- ptr[i * cols + j]로 행렬을 출력한다.
- print2DArray(matrix[0][0], 3, 4); 이런식으로 자주 쓰게된다.
재귀함수
- 자기 자신을 호출하는 함수
- 복잡한 문제를 간단한 하위 문제로 분할
- 수학적 정의나 반복적 구조에 유용
void countdown(int n) {
if (n <= 0) { // 기저 조건 (Base Case)
printf("발사!\\\\n");
return;
}
printf("%d\\\\n", n);
countdown(n - 1); // 재귀 호출
}
int main() {
countdown(5);
return 0;
}
재귀 함수 필수 요소
- 기저 조건 (Base Case)
- 재귀 호출을 멈추는 조건
- 반드시 필요 (없으면 무한 재귀)
- 재귀 호출 (Recursive Call)
- 자기 자신을 호출
- 매개변수가 기저 조건에 가까워져야 함
팩토리얼 계산 예시
#include <stdio.h>
int factorial(int n) {
if (n <= 1) { // 기저 조건
return 1;
}
return n * factorial(n - 1); // 재귀 호출
}
int main() {
int num = 5;
printf("%d! = %d\\\\n", num, factorial(num));
return 0;
}
피보나치 수열 예시
#include <stdio.h>
int fibonacci(int n) {
if (n <= 1) { // 기저 조건
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2); // 재귀 호출
}
int main() {
for (int i = 0; i < 10; i++) {
printf("fibonacci(%d) = %d\\\\n", i, fibonacci(i));
}
return 0;
}
- 재귀함수 단점 : 반복문보다 큰 오버헤드 발생
재귀 vs 반복문
구분 재귀 함수 반복문
| 가독성 | 높음 (수학적 정의와 유사) | 보통 |
| 메모리 사용 | 많음 (스택 사용) | 적음 |
| 실행 속도 | 느림 (함수 호출 오버헤드) | 빠름 |
| 구현 복잡도 | 간단 | 복잡할 수 있음 |
공유 저장소(git, fork) 사용
포크
- 실행 - File - preference - Git : 본인 깃허브 아이디 연결
- File - preference - General : 부모 폴더 연결
- 브런치 란? : 독립 작업을 위한 공간?이다.
- 작업 후 merge로 다시 하나의 브런치로 모은다.
- local changes : 로컬 환경(내 컴퓨터)에서 수정하거나 변경한 내용이다.
- unstaged : 내가 commit하지 않을 내용을 모아놓은 곳
- staged : 내가 commit 할 내용을 모아놓은 곳
- unstaged 파일들을 드래그해서 옮긴다.
- commit subject : 커밋할 바뀐 내용을 적는 텍스트창
- 커밋 : 로컬 저장소에서 현재 시점의 변경된 파일을 저장하는 행위다.
- 언제든 커밋 한 시점으로 돌아갈 수 있다.
- 원하는 파일만 묶어서 커밋할 수 있다.
- 푸쉬 : 커밋한 로컬 저장 파일을 원격 저장소(github)에 업로드한다.
- 풀 : 업데이트된 파일들이 있는 원격 저장소(github)에서 로컬 저장소(내 컴퓨터, 현재 작업 폴더)로 불러온다.
- 원격 저장소 : 아무 곳에서나 사용할 수 있는 저장소
- 로컬 저장소 : 현재 내가 사용하는 저장소(컴퓨터)
- 브런치 히스토리 깃 아이콘 : 원격 저장소(깃허브) 위치
- 브런치 메인(마스터) 아이콘 : 로컬 저장소(현재 작업 컴퓨터) 위치
- 차이가 난다면 현재 작업과 저장된 작업의 진행이 다른것이다.
깃
- 레포지토리 생성
- gitignore : visualstudio
- https 주소 복사
- 포크 - file - clone - 생성
gitignore
- 버전관리에 무시, 빼야하는 파일 및 폴더를 모아놓은 파일이다.
-
- : 모든 내용 포함 명령
- / : 해당 폴더 선택
SFML
- 비주얼 스튜디오 sfml 연결 튜토리얼 : https://www.sfml-dev.org/tutorials/2.6/start-vc.php
- 라이브러리 : 누군가 갖다 사용해놓을 수 있게 특정 기능들을 공개해놓은 기능이다.
- sfml : 사운드, 그래픽 등 공개 라이브러리다.
- 교육용으로 사용되고있다.
- win 대용으로 사용할 수 있다.
- 객체지향 설계로 게임을 개발할 수 있게 도와준다.
- 프로젝트 생성
- .git 있는 폴더에 sln 포함 파일들을 복붙한다.
- 라이브러리 가져오는 법
- 라이브러리가 있는 경로 설정
- 라이브러리 관리자 툴, 어플리케이션 사용
사용법
- 절대경로 : 드라이브부터 폴더 경로가 처음부터 끝까지 고정되어있는 경로다.
- 왠만해서는 이거 말고 상대경로로 써야한다.
- 상대경로 : 현재 경로를 기준으로 폴더 경로를 표시하는 것이다.
- 절대경로에 포함되어있는 현재 경로까지는 안적어도 되고, 그 다음 경로부터 작성되는 경로다.
- . : 현재 형로를 의미한다.
- 현재 폴더안의 파일을 사용할때 사용한다.
- .. : 현재 경로 기준으로 부모 폴더를 의미한다.
- 부모 폴더 안의 파일을 사용할때 사용한다.
- ..\.. : 부모폴더의 부모 폴더를 의미한다.
- 튜토리얼 main 코드 복붙
- 다운받은 sfml 압축 풀기
- 깃 프로젝트 폴더에 복사
- 4번에서 생성된 bin 폴더에 SFML 폴더의 bin 폴더 파일들 전부 복붙
- 프로젝트 우클릭 - 디버그 설정 수정 - 릴리즈 설정도 똑같이 설정한다.
- C/C++
- 일반
- 추가/포함 디렉토리
- SFML-2.6.2\include
- 추가/포함 디렉토리
- 일반
- 링커
- 일반
- 추가 라이브러리 디렉터리
- SFML-2.6.2\lib
- 추가 라이브러리 디렉터리
- 입력
- 추가 종속성
- sfml-graphics-d.lib;sfml-window-d.lib;sfml-system-d.lib;sfml-network-d.lib;sfml-audio-d.lib
- 릴리즈 만 설정 : sfml-graphics.lib;sfml-window.lib;sfml-system.lib;sfml-network.lib;sfml-audio.lib
- 추가 종속성
- 일반
- 구성 속성
- 구성 속성
- 일반
- 출력 디렉터리
- ..\$(MSBuildProjectName)-bin\
- 대상 이름 - 디버깅만
- 뒤에 -d 붙이기
- 출력 디렉터리
- 디버깅
- 작업 디렉터리
- ..\$(MSBuildProjectName)-bin\
- 작업 디렉터리
- 일반
- C/C++
bin 폴더 레포지토리 만들기
- 포크 - file - init new repogitory - 폴더 선택 - 15개 파일 Stage로 옮기고 commit
- 깃허브 - 레포지토리 생성 - 이름 : sfml_timber_bin 작성 후 아무 설정 안건들고 생성 - 깃허브 주소 복사
- 포크 - 좌측 Remotes 우클릭 - Add new repogitory 클릭 - 복사한 주소 복붙되있으면 생성 - push
SFML 시작
- 생성자 : 클래스로 만들어지는 변수들이 프로그램 시작 시 초기화하는 함수다.
- 멤버 변수 및 함수 : 클래스 안에 선언되어있는 함수 및 변수다.
- . : 멤버 접근 연산자 ⇒ 클래스에 선언 및 정의된 변수 및 함수에 접근하는 연산자다.
- 이벤트 : 입력이 들어오는 것에 반응하는 클래스다.