본문 바로가기

Programming Language/C

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

컴퓨터에서 프로그램이 실행되기 위해서는 프로그램이 메모리 공간에 적재되어야 합니다. 메모리에 적재된 이후에는 CPU가 한 줄 한 줄 읽어 내려가며 프로그램을 실행합니다. 또한 프로그램을 실행하기 위해서는 프로그램의 크기를 충당할 수 있을 만큼의 메모리 공간을 확보해야 합니다. 흔히 우리가 사용하는 컴퓨터의 메모리는 8gb 또는 16gb로 구성되어 있는데 특정한 프로그램을 실행하면 그 프로그램이 메모리에 적재가 되어 실행이 되는 것입니다.

기본적으로 컴퓨터의 운영체제는 메모리 공간을 아래 그림과 같이 네 가지로 구분하여 관리합니다.

네 가지로 구분되는 각각의 메모리 속 영역은 서로 다른 기능들을 가지고 있습니다.

  • 코드 영역은 그 이름과 같이 한 줄씩 실행할 수 있는 소스코드입니다.
  • 데이터 영역은 변수 중에서도 전역 변수와 정적 변수를 담고 있는 영역입니다.
  • 힙 영역은 동적 할당 변수를 담고 있는 영역입니다.
  • 스택 영역은 함수마다 포함된 지역 변수와 매개 변수를 담고 있는 영역입니다.

1. 전역변수(Global Variable)

전역변수란 프로그램의 어디서든 접근이 가능한 변수를 뜻합니다. main함수가 실행되기 전에 프로그램의 시작과 동시에 메모리에 할당이 됩니다. 프로그램의 크기가 커질수록 전역 변수로 인해 프로그램이 복잡해질 수 있으니 이 점에 유의하여 활용해야 합니다. 또한, 전역변수는 메모리의 데이터 영역에 적재됩니다.
예제를 통해서 전역변수의 기본적인 활용법에 대해서 알아보겠습니다.

#include <stdio.h>

int a = 5;

int changeValue() {
    a = 10;
}

int main(void) {
    printf("%d\n", a);
    changeValue();
    printf("%d\n", a);
    system("pause");
    return 0;
}

결과:

5
10
계속하려면 아무 키나 누르십시오 . . .

전역변수는 int a = 5; 와 같이 main 함수 바깥에 선언되었습니다. 전역변수이기 때문에 main함수가 아닌 다른 함수인 changeValue() 에서도 바로 접근하여 활용할 수 있는 것입니다.


2. 지역변수(Local Variable)

지역변수란 프로그램에서 특정한 블록에서만 접근할 수 있는 변수를 뜻합니다. 함수가 실행될 때마다 함수 안에 있는 변수들이 메모리에 할당되어 함수가 종료되면 메모리에서 해제됩니다. 또한, 지역변수는 메모리의 스택 영역에 기록됩니다.

아래의 예제를 통해서 지역변수를 활용해보겠습니다. 작성할 예제는 if문 안에서 a를 5라고 선언하여 결과적으로 5를 출력하려고 하는 예제입니다.

#include <stdio.h>

int main(void) {
    int a = 7;
    if (1) {
        int a = 5;
    }
    printf("%d\n", a);
    system("pause");
    return 0;
}

결과

7
계속하려면 아무 키나 누르십시오 . . .

5를 최종적으로 출력하려고 의도했으나 7이 출력되었습니다. 이것은 int a = 5; 라고 선언한 것이 if문이라는 블록 안에서 초기화된 지역변수이기 때문입니다. 기본적으로 블록 안에서 선언된 변수들은 지역변수라고 말할 수 있습니다.

현재 위에 작성된 예제는 두 개의 블록이 있다고 볼 수 있습니다. main함수로 구성된 블록이 있고 그 안에 if문으로 이루어진 블록이 있습니다. if문 블록 안에서 만들어진 것은 if문 안에서만 사용할 수 있습니다. 따라서 if문 바깥에서 출력될 a는 똑같이 바깥에서 선언된 a 변수, 즉 7을 출력하는 것이고 if문이 종료된 순간 메모리에서 5라는 값은 해제된 것입니다.

main함수와 if문 안에 선언된 a는 서로 다른 변수이기 때문에 위 예제에서 최종적으로 5를 출력하기 위해서는 if문 안에서 단순히 a = 5;라고만 작성하여 공통된 a를 활용하게 해야 합니다.


3. 정적 변수(Static Variable)

정적변수란 특정한 블록에서만 접근할 수 있는 변수입니다. 정적변수는 프로그램이 실행될 때 메모리에 할당되어 프로그램이 종료되면 메모리에서 해제됩니다. 프로그램이 실행될 때 메모리에 할당이 되면서 특정한 블록에 접근할 수 있다는 점으로 보아 지역변수와 전역변수의 특징을 모두 가진다고 볼 수 있습니다. 또한 메모리의 데이터 영역에 적재됩니다.

#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;
}

결과:

6
7
8
계속하려면 아무 키나 누르십시오 . . .

process() 함수를 보면 a 앞에 static이라고 붙은 것을 볼 수 있을텐데, 이렇게 static을 선언함으로써 프로그램이 실행과 동시에 메모리에 적재가 됩니다. 이후에 process()를 불러올 때마다 이미 적재가 된 것(static 구문)은 무시를 하고 a에 1을 더해서 출력합니다. 즉, 두 번째 process();를 선언했을 때 static int a는 5가 아니라 6인 상태이고, 세 번째 process();를 선언했을 때 static int a는 6이 아닌 7인 상태입니다.


4. 레지스터 변수(Register Variable)

레지스터 변수란 메인 메모리 대신 CPU의 레지스터를 사용하는 변수입니다. 메인 메모리보다 레지스터가 CPU에 조금 더 가깝기 때문에 훨씬 처리 속도가 빠르다는 장점이 있습니다. 다만, 레지스터는 매우 한정되어 있습니다. 따라서, 실제로 레지스터에서 처리할지를 결정하는 것은 컴파일러가 담당하기 때문에 우리가 레지스터 변수를 사용해도 항상 레지스터에서 처리될지는 장담할 수 없습니다.

#include <stdio.h>

//레지스터 변수를 사용했기 때문에 조금 더 빠를 것이라고 기대할 수 있는 것.
int main(void) {
    register int a = 10, i;
    for (i = 0; i < a; i++) {
        printf("%d ", i);
    }
    system("pause");
    return 0;
}

결과:

0 1 2 3 4 5 6 7 8 9 계속하려면 아무 키나 누르십시오 . . .

레지스터 변수를 사용했기 때문에 조금 더 빠르게 프로그램이 처리되었을 것이라고 기대할 수 있는 것입니다.


5. 함수의 매개변수 처리

함수를 호출할 때 함수에 필요한 데이터를 매개변수로 전달하여 활용했습니다. 전달 방식은 크게 두 가지가 있는데, 값에 의한 전달 방식과 참조에 의한 전달 방식이 있습니다. 값에 의한 전달 방식은 단지 값을 전달하기 때문에 함수 내에서 변수가 새롭게 생성됩니다. 그리고 참조에 의한 전달 방식은 주소를 전달하기 때문에 원래의 변수 자체에 접근할 수 있습니다. 비유를 하자면, 전자는 지역 변수에 가까운 개념이고, 후자는 전역변수에 가까운 개념이라고 볼 수 있습니다.

5.1. 값에 의한 전달 방식

더하기를 실행하는 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;
}

결과:

7
계속하려면 아무 키나 누르십시오 . . .

add() 함수로 두 개의 값을 넣으면 새롭게 두 변수가 메모리 내에 할당되어 처리됩니다. 따라서, 원래 변수의 값에는 영향을 미치지 못하므로 위 코드의 결과는 17이 아닌 7이 됩니다.

값에 의한 전달 방식에 따라 함수로 넘겨 준 매개변수는 함수 안에서 사용되는 함수 전용 변수이기 때문에 원래 기존에 존재했던(main함수 안의 a) a라는 변수에는 영향을 미치지 못합니다. 그러므로 add함수를 불러와 실행해도 main 함수 안의 변수 a가 담고 있는 값은 여전히 7이기 때문에 7이 출력되고 add함수 안에서만 값이 17로 변경되는 것입니다.

5.2. 참조에 의한 전달 방식

참조에 의한 전달 방식은 간접 참조 연산자를 사용한다는 점이 특징입니다. 다시 말해, 매개변수로 값을 전달하는 것이 아니라 변수의 주소를 전달합니다. 이러한 방식을 이용하면 원래 변수의 값(main함수 안의 a)에 접근하여 값을 변경할 수 있습니다.

#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;
}

결과:

17
계속하려면 아무 키나 누르십시오 . . .