컴퓨터가 변수를 처리하는 방법

April·2021년 10월 4일
0

🌹CS

목록 보기
13/17
post-thumbnail

개인 공부를 위해 작성했습니다.

현업에서 일 하고 CS의 중요성을 느끼면서 배우게 된 CS의 기초들..
그 중 오늘은 컴퓨터가 변수를 처리하는 방법에 대해 정리해 본다.


🚀 What I Will Learn

  1. C언어에서 사용하는 다양한 변수를 처리하는 방법 익히기
  2. 지역 변수, 전역 변수, 레지스터 변수 등에 대해 이해하기
  3. 특정한 함수에 값을 전달하거나 주소를 전달하는 방법 이해하기

컴퓨터가 변수를 처리하는 방법

✔️ 프로그램 메모리 주소

  • 컴퓨터에서 프로그램이 실행되기 위해서는 프로그램이 메모리에 적재(Load)되어야 한다
  • (당연히) 프로그램 크기를 충당할 수 있을 만큼의 메모리 공간이 있어야 한다
    • 흔히, 컴퓨터들의 메모리 사양은 8GB, 16GB, 32GB 정도 되는데
    • 특정한 프로그램을 더블 클릭해서 실행하면 그 프로그램이 메모리에 적재되어 실행된다
  • 일반적인 컴퓨터의 운영체제는 메모리(RAM) 공간을 네 가지로 구분하여 관리한다.
    • 코드 영역: 코드를 한줄씩 실행시키는 영역
    • 데이터 영역: 전역변수 혹은 정적변수를 담고 있다.
      프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리가 소멸되는 영역
    • 힙 영역: 개발자가 할당/해제하는 메모리 공간이다. 가비지 컬렉터가 자동으로 해제한다.
      이 공간에 메모리 할당하는 것을 동적 할당(Dynamic Memory Allocation)이라고도 부른다.
    • 스택 영역: 프로그램이 자동으로 사용하는 임시 메모리 영역.
      함수 호출 시 생성되는 지역 변수와 매개변수가 저장되는 영역이고,
      함수 호출이 완료되면 사라진다.
      코드 영역데이터 영역힙 영역스택 영역
      소스 코드젼역 변수동적 할당 변수지역 변수
      정적 변수매개 변수

참고로, HEAPSTACK영역은 사실 같은 공간을 공유한다.
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();   // 6
	process();   // 7
	process();   // 8
	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;
	// system("pause"); 함수 내부에서 출력한다면 결과값은 17
}

int main(void) {
	int a = 7;
	add(a, 10);
	printf("%d\n", a);  // 값에 의한 전달 방식을 사용했으므로 결과값은 7
	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);  // 17
	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언어에서는 전역변수, 지역변수 등 다양한 종류의 변수가 사용된다
  • 함수에 데이터를 전달하는 방법은 두 가지가 있는데
    • 값을 전달하는 방법
    • 주소를 전달하는 방법 이다
  • 포인터는 특정한 변수가 메모리 상에 존재하는 위치 주소를 저장.
    • 특정한 메모리 주소에 바로 접근할 수 있으므로 조심스럽게 사용하자!!
profile
🚀 내가 보려고 쓰는 기술블로그

0개의 댓글