개인 공부를 위해 작성했습니다.
현업에서 일 하고 CS의 중요성을 느끼면서 배우게 된 CS의 기초들..
그 중 오늘은 컴퓨터가 변수를 처리하는 방법에 대해 정리해 본다.
🚀 What I Will Learn
- C언어에서 사용하는 다양한 변수를 처리하는 방법 익히기
- 지역 변수, 전역 변수, 레지스터 변수 등에 대해 이해하기
- 특정한 함수에 값을 전달하거나 주소를 전달하는 방법 이해하기
컴퓨터가 변수를 처리하는 방법
✔️ 프로그램 메모리 주소
- 컴퓨터에서 프로그램이 실행되기 위해서는 프로그램이 메모리에 적재(Load)되어야 한다
- (당연히) 프로그램 크기를 충당할 수 있을 만큼의 메모리 공간이 있어야 한다
- 흔히, 컴퓨터들의 메모리 사양은 8GB, 16GB, 32GB 정도 되는데
- 특정한 프로그램을 더블 클릭해서 실행하면 그 프로그램이 메모리에 적재되어 실행된다
- 일반적인 컴퓨터의 운영체제는 메모리(RAM) 공간을 네 가지로 구분하여 관리한다.
코드 영역
: 코드를 한줄씩 실행시키는 영역
데이터 영역
: 전역변수 혹은 정적변수를 담고 있다.
프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리가 소멸되는 영역
힙 영역
: 개발자가 할당/해제하는 메모리 공간이다. 가비지 컬렉터가 자동으로 해제한다.
이 공간에 메모리 할당하는 것을 동적 할당(Dynamic Memory Allocation)이라고도 부른다.
스택 영역
: 프로그램이 자동으로 사용하는 임시 메모리 영역.
함수 호출 시 생성되는 지역 변수와 매개변수가 저장되는 영역이고,
함수 호출이 완료되면 사라진다.
코드 영역 | 데이터 영역 | 힙 영역 | 스택 영역 |
---|
소스 코드 | 젼역 변수 | 동적 할당 변수 | 지역 변수 |
| 정적 변수 | | 매개 변수 |
참고로, HEAP
과 STACK
영역은 사실 같은 공간을 공유한다.
HEAP
이 메모리 위쪽 주소부터 할당되면 STACK
은 아래쪽부터 할당되는 식이다. 그래서 각 영역이 상대 공간을 침범하는 일이 발생할 수 있는데 이를 각각 HEAP OVERFLOW
, STACK OVERFLOW
라고 칭한다.
Stack
영역이 크면 클 수록 Heap
영역이 작아지고, Heap
영역이 크면 클수록 Stack
영역이 작아진다.
✔️ 변수의 종류와 메모리 할당
:: 전역변수(Global Variable)
- 전역변수란 프로그램의 어디서든 접근 가능한 변수
- 프로그램의 시작과 동시에 메모리에 할당되고
- 프로그램의 크기가 커질수록 전역변수로 인해 프로그램이 복잡해질 수 있다
- 메모리의 데이터(Data) 영역에 적재된다
:: 지역변수(Local Variable)
- 프로그램의 특정한 블록(block)/스코프(scope)에서만 접근할 수 있는 변수
- 함수가 실행될 때마다 메모리에 할당되어 함수가 종료되면 메모리에서 해제된다
- 메모리의 스택(Stack) 영역에 기록된다
:: 정적변수(Static Variable)
전역변수와 지역변수의 특징을 모두 가지고 있다
- 프로그램의 특정한 블록(block)/스코프(scope)에서만 접근할 수 있는 변수
- 프로그램이 실행될 때 메모리에 할당되어 프로그램이 종료되면 메모리에서 해제된다
- 메모리의 데이터(Data) 영역에 기록된다
#include <stdio.h>
void process() {
static int a = 5;
a = a + 1;
printf("%d\n", a);
}
int main(void) {
process();
process();
process();
system("pause");
return 0;
}
:: 레지스터 변수(Register Variable)
- 메인 메모리 대신 CPU의 레지스터를 사용하는 변수
- 메모리 보다는 레지스터가 CPU에 더 가깝기 때문에 처리 속도가 빠르다
- 즉, 레지스터 변수를 사용했을 때 속도적으로 유리하다.
- 다만, 레지스터는 매우 한정되어 있으므로 실제로 (항상) 레지스터에서 처리될지는 장담할수 없다;;;
#include <stdio.h>
int main(void) {
register int a = 10, i;
for (i = 0; i < a; i++) {
printf("%d\n", a);
}
system("pause");
return 0;
}
:: 함수의 매개 변수가 처리될 때
- 함수를 호출할 때, 함수에 필요한 데이터를 매개변수로 전달
- 전달 방식은
1) 값에 의한 전달 방식
과
2) 참조에 의한 전달 방식
이 있다
값에 의한 전달 방식
은 단지 값을 전달하므로 함수 내에서 변수가 새롭게 생성된다 → 비유하자면 지역변수와 같다
참조에 의한 전달 방식
은 주소를 전달하므로 원래의 변수 자체에 접근할 수 있다 → 비유하자면 전역변수와 같다
📍 값에 의한 전달 방식(add 함수) 예시
- add 함수로 두 개의 값을 넣으면 새롭게 두 변수가 메모리 내에 할당되어 처리
- 따라서 원래 변수의 값에는 영향을 미치지 못한다
#include <stdio.h>
void add(int a, int b) {
a = a + b;
}
int main(void) {
int a = 7;
add(a, 10);
printf("%d\n", a);
system("pause");
return 0;
}
📍 참조에 의한 전달 방식(add 함수) 예시
- 매개변수로 값을 전달하는 것이 아닌, 변수의 주소 즉, 📎포인터 값을 전달
- 원래 변수의 값에 접근하여 값을 변경할 수 있다
#include <stdio.h>
void add(int *a) {
(*a) = (*a) + 10;
}
int main(void) {
int a = 7;
add(&a);
printf("%d\n", a);
system("pause");
return 0;
}
📎 포인터(Pointer)란?
포인터의 개념
- 포인터는 특정한 변수 자체가 존재하는 메모리의 주소의 값을 가진다
- 단순히 주소값만 저장하는게 아닌, 어떤 자료형인지도 포함하여 저장
- 따라서, 기존 a를 이용해서도 5라는 값을 찾을 수 있지만, 포인터 변수인 b를 이용해서도 5라는 값을 찾을 수 있다.
이름 | 설명 |
---|
주소 연산자(&) | 변수 앞에 붙어서 변수의 메모리 시작 주소 값을 구함 |
포인터(*) | 포인터 변수를 선언할 때 사용 |
간접 참조 연산자(*) | 선언된 포인터 변수가 가리키는 변수를 구함 |
포인터의 강력한 기능
- 포인터는 컴퓨터 시스템의 특정한 메모리에 바로 접근할 수 있다
- 따라서 기존에 존재하던 중요한 메모리 영역에 접근하지 않도록 해야 한다
- 아래의 코드는 굉장히 위험한 코드 예시이다... 😱😱
int *a = 0x12345678;
*a = 0;
- 다중 포인터의 개념도 있다. 예시) 포인터의 포인터
int *a = 5;
int *b = &a;
int **c = &b;
- 배열과 포인터는 사실 내무적으로 거의 동일하다. 배열을 선언한 이후에는 그 이름 자체를 포인터 변수처럼 사용할 수 있다.
✨ tl;dr
- C언어에서는 전역변수, 지역변수 등 다양한 종류의 변수가 사용된다
- 함수에 데이터를 전달하는 방법은 두 가지가 있는데
값을 전달하는 방법
과
주소를 전달하는 방법
이다
- 포인터는 특정한 변수가 메모리 상에 존재하는 위치 주소를 저장.
- 특정한 메모리 주소에 바로 접근할 수 있으므로 조심스럽게 사용하자!!