7-2-나.레지스터 변수

레지스터형 변수는 앞에서 논한 세 개의 기억 부류와 좀 다른 유별난 점이 있다. 지역, 전역, 정적변수들은 정적 데이터 영역이든 스택이든 어쨌든 메모리의 한 구석에 생성되지만 레지스터형 변수는 메모리가 아닌 CPU의 레지스터에 저장된다. 레지스터(Register)란 CPU를 구성하는 부품 중 하나이며 CPU가 데이터를 처리하기 위해 사용하는 임시 작업장이라고 생각하면 된다.

컴퓨터의 가장 핵심 부품인 CPU의 한 가운데에 있는 기억 장소이기 때문에 레지스터의 속도는 메모리와 비교가 되지 않을 정도로 빠르다. 값을 읽거나 쓰는데 수십억분의 1초 정도밖에 걸리지 않는다. CPU의 종류에 따라 다르지만 레지스터는 보통 10개~20개 정도밖에 없는 아주 귀한 기억 장소인데 여기에 변수를 저장하면 이 변수를 참조하는 문장의 속도가 빨라진다.

레지스터의 크기는 CPU의 비트수를 결정하는 중요한 기준인데 레지스터가 32비트면 32비트 CPU라고 부른다. 386이후부터 최신의 팬티엄 4까지 현재까지 우리가 사용하는 CPU는 대부분 32비트이므로 레지스터들도 전부 32비트이고 따라서 레지스터에 저장할 수 있는 변수의 타입은 int, unsigned, 포인터 형 등의 32비트형뿐이다. double같은 실수형은 저장할 수 없으며 구조체나 배열 따위는 당연히 안된다. 에러는 아니지만 지정해 봐야 무시당한다.

CPU의 레지스터 개수가 많지 않기 때문에 레지스터형 변수는 두 개까지만 선언할 수 있다. 컴파일러나 플랫폼에 따라 레지스터형 변수를 위해 할당하는 레지스터가 다른데 인텔 플랫폼에서는 많이 사용되지 않는 ESI, EDI 레지스터를 사용한다. 만약 세 개 이상의 레지스터형 변수를 선언하면 최초 두 개까지만 레지스터형이 되고 나머지는 지역변수가 된다.

 

register int r;           // 레지스터형 변수 r선언

register double d;        // 실수는 레지스터형 변수가 될 수 없음. 지역변수로 선언된다.

register a,b,c;             // a,b만 레지스터형 변수가 되고 c는 지역변수가 된다.

 

레지스터는 한정된 자원이기 때문에 일시적으로 사용할 지역변수에만 지정할 수 있으며 전역변수에는 레지스터 기억 부류를 지정할 수 없다. 프로그램과 생명을 같이 하는 전역변수가 레지스터 하나를 차지한다면 프로그램 실행중인 동안 레지스터 하나가 묶여 버리게 될 것이다. 전역변수에 register 기억 부류를 지정하면 명백한 에러로 처리된다. 지역변수 또는 함수의 형식 인수에 대해서만 이 기억 부류를 사용할 수 있다.

레지스터형 변수를 사용하는 이유는 조금이라도 더 빠른 속도를 얻기 위해서이다. 대규모의 루프를 돌린다거나 할 때 루프 제어 변수를 레지스터형으로 선언하면 이 변수의 읽기, 증감 속도가 빨라지므로 전체 루프의 실행 속도가 빨라질 것이다.

 

register int i, j;

for (i=0;i<10000000;i++) {

     for (j=0;j<1000;j++)

 

총 루프 반복 회수는 100억번인데 이런 경우에 레지스터형 변수를 쓸 때와 지역변수를 쓸 때 속도 차이가 많이 나게 된다. 이런 경우가 아니라면 레지스터형 변수로 속도상의 이득을 볼 수 있는 경우는 그리 흔하지 않다. 요즘 CPU나 메모리가 워낙 빨라져서 루프를 도는 정도의 차이는 거의 실감하기 어렵다.

속도상의 차이 외에는 일반적인 지역변수와 다른 점이 전혀 없다. 레지스터형 변수는 응용 프로그램을 고도로 최적화할 때 가끔 사용되는데 사실 이 기억 부류는 몰라도 별로 손해볼 것이 없다. 왜냐하면 별도의 지정이 없더라도 컴파일러가 남는 레지스터가 있으면 알아서 레지스터형으로 만들 정도로 충분히 지능적이기 때문이다.

레지스터형 변수가 처리되는 방식은 컴파일러에 종속적이다. C 스팩에는 register 지정자가 있으면 이 변수를 레지스터형으로 처리하라고 권고하고 있지만 컴파일러 구현상 남는 레지스터가 없으면 이 변수는 지역변수가 될 수밖에 없다. 비주얼 C++은 register 지정자를 완전히 무시하며 전역 최적화 옵션에 따라 자동으로 레지스터형 변수를 관리한다.

레지스터형 변수는 메모리에 생성되는 것이 아니므로 &연산자는 사용할 수 없다. 레지스터는 CPU 내부에 있기 때문에 번지를 가지지 않으므로 &연산자로 이 변수의 메모리 주소를 조사할 수 없다. 그러나 레지스터형 포인터 변수가 번지를 기억할 수는 있으므로 *연산자를 사용하는 것은 가능하다. C 스팩에는 레지스터형 변수와 &, * 연산자의 관계가 이렇게 규정되어 있으며 상식적으로 이해가 갈 것이다.

하지만 비주얼 C++을 비롯한 최신 컴파일러들은 이 스팩을 따르지 않는다. 만약 레지스터형 변수에 & 연산자를 한 번이라도 사용하면 이를 에러로 처리하지 않고 아예 이 변수를 지역변수로 만들어 버림으로써 주소를 가지도록 한다. 레지스터형 변수에 &연산자를 사용한 것은 분명히 개발자의 실수인데 이런 실수를 컴파일러가 알아서 수정해 주는 것이다. 어차피 레지스터형 변수와 지역변수는 근소한 속도 차이외에는 아무 차이가 없으므로 컴파일러가 사용자의 지시를 무시하고 기억 부류를 바꾸어도 별 문제가 없다.