오늘의 기술 키워드
매개변수 (Parameter)
매개변수는 함수가 외부로부터 값을 전달받기 위해 사용하는 특별한 지역변수라고 할 수 있다.
함수를 정의할 때 괄호 안에 선언하며, 함수가 호출될 때 값을 전달받는다.
- 역할: 외부 데이터를 함수 내부로 가져오는 통로 역할을 합니다.
- 생성 시기: 함수가 호출될 때 생성되고, 함수 실행이 끝나면 사라집니다.
void printSum(int a, int b) { // 여기서 a와 b가 매개변수입니다.
int sum = a + b;
printf("합계: %d\n", sum);
}
int main() {
printSum(10, 20); // 10과 20이 a와 b에 전달됩니다.
return 0;
}
이 예시에서 printSum 함수는 a와 b라는 매개변수를 통해 main 함수로부터 10과 20이라는 값을 전달받아 합계를 계산한다
지역변수 (Local Variable)
지역변수는 함수나 특정 코드 블록(중괄호 {}) 내에서 선언되어 그 안에서만 사용 가능한 변수.
다른 함수에서는 접근할 수 없다.
- 역할: 함수 내부에서 필요한 데이터를 저장하고 계산하는 데 사용됩니다.
- 생성 시기: 변수가 선언된 코드 블록이 실행될 때 생성되고, 블록 실행이 끝나면 사라집니다.
void calculate() {
int x = 10; // 여기서 x는 지역변수입니다.
int y = 20; // y도 지역변수입니다.
printf("x: %d, y: %d\n", x, y);
}
int main() {
calculate();
// printf("%d", x); // 에러! x는 calculate 함수 안에서만 유효합니다.
return 0;
}
이 예시에서 x와 y는 calculate 함수 내에서만 존재하고 사용된다. main 함수에서는 x에 접근할 수 없다.
비유로 설명
매개변수는 "손님에게서 주문을 받는 메뉴판"이고, 지역변수는 "주방 안에서 요리를 위해 사용하는 재료나 도구"
메뉴판(매개변수)은 손님(외부)의 주문(값)을 받아 주방(함수)에 전달하고, 주방에서는 재료와 도구(지역변수)를 사용해 음식을 만듦
Call by Value와 Call by Reference
함수에 데이터를 전달하는 두 가지 방식.
Call by Value
콜 바이 밸류는 값(Value)을 복사해서 전달하는 방식.
- 동작 방식: 함수를 호출할 때, 전달하려는 변수의 '값'을 복사해서 매개변수에 넘겨줌.
- 특징: 함수 내부에서 매개변수의 값을 변경해도, 원래 변수의 값은 변하지 않음. 왜냐하면 함수는 원본이 아닌, 복사된 값을 가지고 작업하기 때문.
- 비유 설명: 친구에게 중요한 서류의 '사본'을 주는 것과 같습니다. 친구가 그 사본에 낙서를 해도, 내가 가진 원본 서류는 그대로 깨끗한 것과 같음.
#include <stdio.h>
void increase(int num) { // num은 x의 복사본입니다.
num = num + 1;
printf("함수 안: %d\n", num); // 11
}
int main() {
int x = 10;
increase(x); // x의 값 10이 increase 함수로 복사됩니다.
printf("함수 밖: %d\n", x); // 10 (변화 없음)
return 0;
}
Call by Reference
콜 바이 레퍼런스는 주소(Reference, Memory Address)를 전달하는 방식
- 동작 방식: 함수를 호출할 때, 변수가 저장된 '메모리 주소'를 매개변수에 넘겨준다. 함수는 이 주소를 이용해 원본 변수에 직접 접근해서 값을 변경할 수 있다.
- 특징: 함수 내부에서 매개변수를 통해 값을 변경하면, 원본 변수의 값도 함께 변경됨.
- 비유 설: 친구에게 '집 열쇠'를 주는 것과 같습니다. 친구는 그 열쇠로 집에 들어가서 가구를 마음대로 바꿀 수 있고, 내가 다시 집에 와보면 변경된 상태 그대로인 것과 같다.
- 예시 (C 언어): C 언어는 엄밀히 말해 콜 바이 레퍼런스를 직접 지원하지 않는다. 대신, 포인터(Pointer)를 사용해서 이와 같은 효과를 낼 수 있다. 이를 '콜 바이 포인터(Call by Pointer)'라고 부르기도 한다..
#include <stdio.h>
void increase(int *num) { // num은 x의 주소를 가리키는 포인터입니다.
*num = *num + 1; // *num은 주소에 있는 원본 값에 접근합니다.
printf("함수 안: %d\n", *num); // 11
}
int main() {
int x = 10;
increase(&x); // x의 주소를 increase 함수로 전달합니다.
printf("함수 밖: %d\n", x); // 11 (값이 변경됨)
return 0;
}
위 코드에서 &x는 x의 메모리 주소를 의미하고, *num은 그 주소에 저장된 값을 의미.
메모리 할당 (Memory Allocation)
메모리 할당은 프로그램이 실행되는 동안 필요한 데이터를 저장하기 위해 컴퓨터의 메모리 공간을 확보하는 과정.
컴퓨터의 메모리는 마치 큰 창고와 같아서, 프로그램이 창고에 필요한 물건(데이터)을 보관할 공간을 요청하는 것이라고 생각할 수 있다. C언어에서 메모리 할당은 크게 두 가지 방식으로 나뉨.
1. 정적 메모리 할당 (Static Memory Allocation)
정적 할당은 프로그램이 시작되기 전에 메모리 크기가 결정되는 방식.
- 언제 사용? 컴파일러가 변수의 크기를 미리 알 수 있을 때 사용.
- 어디에 할당? 주로 스택(Stack) 영역에 할당됨. 스택은 함수 호출과 관련된 지역 변수들이 순서대로 쌓이는 공간.
- 특징:
- 빠르다. 미리 정해진 규칙에 따라 할당되므로 효율적.
- 크기 변경이 불가능. 프로그램 실행 중에 할당된 메모리 크기를 바꿀 수 없다.
- 자동으로 해제됨. 함수 실행이 끝나면 할당된 메모리가 자동으로 정리됨.
int number = 10; // 4바이트 정수 공간이 미리 할당됩니다.
int arr[10]; // 40바이트 배열 공간이 미리 할당됩니다.
위 변수들은 main 함수가 시작될 때 스택에 필요한 공간이 마련되고, main 함수가 종료될 때 자동으로 사라짐.
2. 동적 메모리 할당 (Dynamic Memory Allocation)
동적 할당은 프로그램이 실행되는 동안 필요한 만큼 메모리 크기를 결정하는 방식.
- 언제 사용? 컴파일러가 변수의 크기를 미리 알 수 없을 때 사용됨. 예를 들어, 사용자의 입력에 따라 배열의 크기가 달라져야 할 때 유용함.
- 어디에 할당? 힙(Heap) 영역에 할당됨. 힙은 사용자가 원하는 크기만큼 자유롭게 메모리를 할당하고 해제할 수 있는 공간임.
- 특징:
- 느립니다. 메모리를 할당하고 해제하는 과정이 추가적으로 필요.
- 크기 변경이 가능합니다. 프로그램 실행 중에 메모리 크기를 조절할 수 있다.
- 수동으로 해제해야 합니다. 사용자가 free() 함수를 이용해 직접 메모리를 해제하지 않으면, 메모리가 계속 쌓여서 프로그램이 비정상적으로 종료될 수 있다. 이를 메모리 누수(Memory Leak)라고 한다.
#include <stdlib.h> // malloc, free 함수 사용을 위해 필요
int main() {
int* arr;
int size = 10;
// 10개의 정수(40바이트)를 힙에 동적으로 할당
arr = (int*)malloc(sizeof(int) * size);
// 할당된 메모리 사용
// ...
// 사용이 끝난 메모리 해제
free(arr);
return 0;
}
이 예시에서 malloc() 함수는 힙 영역에 원하는 크기만큼 메모리를 할당하고, 그 주소를 반환함. 할당된 메모리는 free() 함수를 호출해야만 해제된다.
질문 & 깨달음
1. 매개변수 vs. 지역변수: 왜 따로 존재할까?
의문 포인트: main 함수에서 변수 선언하고 다른 함수에서 쑬 수 없나? 매개변수라는 걸 굳이 써야 하나요?
깨달음 포인트: 함수의 독립성을 위해.
- 모든 함수가 다른 함수의 변수를 마음대로 가져다 쓸 수 있다면, 프로그램이 복잡해질수록 어떤 변수가 어디서 변경되었는지 추적하기가 매우 어려워진다.
- 매개변수는 함수가 외부로부터 필요한 데이터만 "정해진 통로"로 전달받게 하는 규칙. 마치 주문받은 재료만으로 요리하는 주방처럼, 함수는 자신에게 전달된 데이터만 가지고 작업을 수행한다.
- 지역변수는 함수 내부에서만 사용되는 "작업대"와 같다(?). 이 작업대 위에 놓인 도구들은 다른 작업실(다른 함수)에 영향을 주지 않는다.
- 이러한 독립성 덕분에 함수는 재사용하기 쉽고, 버그를 찾기도 훨씬 수월해진다.
2. 콜 바이 밸류 vs. 콜 바이 레퍼런스: 언제 어떤 걸 써야 할까?
의문 포인트: 함수에 변수를 넘겼는데 왜 값이 안 바뀌는가? 혹은 값이 바뀌었는데 왜 이렇게 되는 거지?
깨달음 포인트: 함수의 목적에 따라 다른 방식을 사용함.
- 콜 바이 밸류 (Call by Value): 원본 보호가 목적일 때 사용함. 함수가 전달받은 값으로 계산만 하고, 원래의 데이터는 건드리지 않아야 할 때 유용하다다.
- 예: 두 숫자의 합을 계산해서 반환하는 함수. 원본 값은 그대로 두고, 결과만 알려주면 된다.
- 콜 바이 레퍼런스 (Call by Reference): 원본 변경이 목적일 때 사용함. 함수가 여러 변수의 값을 한 번에 변경해야 하거나, 함수의 결과로 여러 값을 반환해야 할 때 사용함. (C에서는 포인터를 이용)
- 예: 두 변수의 값을 서로 바꾸는 함수. swap 함수는 두 변수의 주소를 받아와서 직접 값을 변경해야 한다.
이 두 가지 방식의 차이를 이해하는 것은 메모리(주소)의 개념을 이해하는 것과 직결되므로 잘 공부해야한다고 느꼈다.
3. 메모리 할당 (정적 vs. 동적): 왜 복잡하게 해야 할까?
의문 포인트: int arr[10]; 이렇게 배열을 쓰면 편한데, 왜 malloc 같은 걸 써서 복잡해지는가?
깨달음 포인트: 프로그램의 유연성을 확보하기 위해서.
- 정적 할당: 컴파일 시점에 크기가 정해져 있다. 프로그램이 실행되기 전에 "10칸짜리 창고"를 미리 만들어 놓는 것과 같다. 빠르고 편리하지만, 10칸보다 더 많이 필요하거나 적게 필요할 때 낭비가 발생함.
- 동적 할당: 프로그램 실행 중에 "필요할 때, 필요한 만큼의 창고"를 만드는 것과 같다. 사용자의 입력에 따라 배열의 크기가 달라질 때, 정적 할당으로는 불가능한 작업을 수행할 수 있다. 예를 들어, 사용자가 100개의 데이터를 입력할지, 10,000개의 데이터를 입력할지 모를 때 매우 유용하다.
- 이때 malloc 함수는 힙(Heap)이라는 자유로운 메모리 공간에서 창고를 만들어주고, 그 창고의 시작 주소를 알려준다. 이 주소를 저장하기 위해 포인터가 필수적으로 사용된다. 따라서 포인터, 동적 할당은 떼려야 뗄 수 없는 관계다.
오늘 좀 헷갈렸던 이 세 가지 개념은 각각 독립된 것처럼 보이지만, '메모리'라는 공통된 기반 위에서 서로 연결되어 작동하는 것을 알게 됐다. 기초를 제대로 해두지 않으면 이후에 더 힘들 것으로 보여 꼼꼼히 채워야겠다는 생각을 했다.
오늘의 연결 태그
#C언어 #메모리 #변수 #포인터 #함수
오늘의 메모 한 줄
포인터, 매개변수, 메모리 할당은 결국 데이터를 효율적으로 다루기 위해 '주소'를 활용하는 것들로, 잘 배워두자.
'C++' 카테고리의 다른 글
| [TIL_C] 클래스 (0) | 2025.08.21 |
|---|---|
| [TIL_C++] 상태창 구현해보기 (0) | 2025.08.20 |
| [TIL_C++] 반복문 for, while 기초 (2) | 2025.08.18 |
| [TIL_C++] 선언, 초기화라는 '단어' 그리고 배열의 임의접근 (3) | 2025.08.14 |
| [TIL_C++] Hello World!로 알아보는 코드 작성법 (4) | 2025.08.13 |