포인터 (프로그래밍)
이 문서의 {{#은는:내용}} 출처가 분명하지 않습니다. (2010년 11월) |
포인터(pointer)는 프로그래밍 언어에서 다른 변수, 혹은 그 변수의 메모리 공간주소를 가리키는 변수를 말한다. 포인터가 가리키는 값을 가져오는 것을 역참조라고 한다.
포인터는 어셈블리어, C, C++, 파스칼 등 하위 레벨까지 제어할 수 있는 언어에서 주로 많이 쓰이며, 모듈라-2, 에이다와 같은 언어에서는 극히 제한적으로 사용되고, 자바, 에펠 등에서는 완전히 숨겨져 사용할 수 없다.
첫 번째 부류의 언어에서는 포인터를 메모리의 임의의 주소를 가리키도록 할 수 있으며 포인터의 연산도 가능하다.
일반적으로 포인터는 메모리 주소로 바꿀 수 있다. 포인터는 다른 변수나 함수를 가리키도록 사용된다.
C 언어의 포인터
모든 변수는 메모리에 값을 저장한다. const와 같은 고정값 변수 외의 모든 변수는 메모리 중에 RAM에 할당된다. 이러한 메모리의 공간을 구별하는 것이 메모리 주소값이다. 주소로 각각의 위치를 구별한다. 포인터 변수 모두는 메모리의 주소를 지정하는 값을 가진다. 데이터가 존재하는 주소값을 사용하여 액세스한다. 즉, 어떤 번지의 메모리에 값을 쓰거나 또는 읽어 오는 방식이다. 정적변수 역시 메모리에 배치되고 결국은 주소값을 가질 것이다. 그러나 차이점은 기계어 코드에 주소값을 고정하여 액세스된다. 그러나 포인터 변수는 주소값을 가지고 액세스하기 때문에 임의의 위치를 바꿀 수도 있게 된다. 전역변수의 정적변수는 기계어 코드에 주소값을 고정하는 방식이 일반적이다. 지역변수는 스택 또는 CPU의 레지스터를 써서 위치값을 설정한다. 포인터 변수는 메모리에 주소값을 저장하는 방식이기 때문에 이 주소값을 읽어 실제 데이터를 액세스한다. 메모리 액세스 모드 중에 직접주소방식으로 데이터를 액세스한다.
메모리의 주소는 CPU을 설계한 설계 기준에 따라 주소값의 길이와 방식이 결정된다. 일반적인 용도의 대부분의 CPU는 메모리를 지정하는 길이(비트수)는 동일하다. RAM이든 ROM/FLASH 이든 모든 주소는 같다. MCU(8051,...)은 오히려 많은 경우 메모리 영역을 나누어 다른 주소체계를 사용한다. 8051은 내부의 256바이트 내에 변수를 할당한다. 256바이트는 적기 때문에 많은 데이터를 처리하기 위해 변수는 잡을 수 없다. 많은 양의 데이터를 저장하기 위해 16비트의 저장 공간을 갖는 주소체계를 같이 사용한다. 그리고 양쪽의 액세스는 기계어 코드를 분리 해서 액세스한다. 이럴 경우는 주소값이 8비트 또는 16비트가 필요하다. C언어 컴파일러에서 이를 지정할 수 있는 방법을 제시한다.
포인터 변수의 유연성은 프로그램 작성의 유연성과 연관된다. C언어가 UNIX 계열의 OS를 작성할 때 사용하였으므로 커널의 프로그램 소스를 보면 상당히 많은 부분 포인터 변수를 볼 수 있다. 유연성은 경우에 따라서 단점으로도 작용할 수 있다. 포인터 값에 따라 정의되지 않는 메모리 영역을 액세스할 수 있기 때문이다. 물론 정적 변수도 스스로의 배열 등의 공간 밖을 액세스할 수 있지만, 포인터를 사용하면 이것이 좀 더 복잡해진다.
선언문에서 가리키고자 하는 변수의 자료형을 맞추어 변수명 앞이나 자료형 뒤나 그 중간에 *(별표)를 붙이면(char* a; == int * pa; == double *exm;) 포인터 변수가 된다.
이렇게 선언된 포인터 변수가 실행문에서는, 일반 자료형 변수를 선언문에서 int a;라고 선언하여 변수 a가 정수형이라고 선언하고, 실행문에서는 int를 안 쓰고 변수명 a만으로 사용하듯이, *을 빼고 변수명만 사용하여 처음 접하는 초보자들이 *이 없어 혼동할 수 있으니 선언문에서와 실행문에서의 사용을 구분해야 한다.
중요한 조건은 자료형을 동일형으로 유지해야 한다. 포인터의 사용목적은 어느 변수를 그 변수명으로 가르키지 않고 그 주소값으로 가르켜서 그 변수의 값(자료값)을 간접참조(역참조)하는 것이므로 정수형 4 바이트 변수를 가르켰는데 같은 변수명이라도 문자형이나 실수형 변수에 연결되어 간접참조하여 값을 읽어오면 기억공간(memory)의 크기가 다르므로 문자형의 1 바이트 이후의 3 바이트를 더 읽거나, 실수형 8 바이트 내의 4 바이트만을 읽어오므로 크기(바이트 수)가 다를 뿐만 아니라 기록되어 있는 내용(값)이 다르므로 자료형이 가리켜지는 변수와 포인터 변수가 같아야 한다.
일반 변수의 주소(주소값)을 알아보려면 변수명에 &를 붙이면 된다(&a). 이것은 포인터가 가진(가르킨, 포인터의 자료값인) 주소이므로 int a;로 정수형 변수를 선언했으면 같은 자료형 정수형으로 int *pa;로 a를 가리키는 포인터 pa를 선언하면 실행문에서는 pa == &a이다.
실행문에서 *은 일반 연산자로는 곱셈을 하고, 참조 연산자로는 해당 주소의 값(자료값)을 간접참조(역참조)하므로 *&a == *pa이다. 결국 a == *&a == *pa이다. (값이 같다는 의미로 == 를 사용)
교체(swap)함수에서 대표적으로 간접참조(call by reference)하는 경우처럼 *pa = xx로 포인터에 참조 연산자를 붙여 값을 참조한다는 표시를 하고 일반 변수의 값을 할당할 때와 같이 대입 연산자 =를 이용하여 간접참조로 가르킨 변수의 값을 xx로 변경한다.
위의 설명을 간략히 하면 아래와 같다.
포인터 - 주소값(간접참조하기 위한 변수의 주소)을 저장하는 변수
포인터 선언 - 선언문에서 주소를 저장하는 변수와 같은 자료형 명시하고 별을 변수명 앞에 붙여 표시
포인터 사용 - 실행문 내에서는 별표 없이 변수명 만 사용(일반 변수들을 선언문에서 자료형 지정하고 실행문에서 변수명 만 사용과 동일)
주소 - 공간의 첫주소(첫주소값)를 말하며 문자형 자료는 1 바잍이니 그 자체, 정수형 자료를 갖는 변수의 주소는 4 바잍의 첫주소가 전체 4 바잍의 공간을 의미, 이중 실수형 자료를 갖는 변수의 주소는 8 바잍의 첫주소가 전체 8 바잍의 공간을 의미, 표시
직접참조: 변수에 접근하여 자료값을 읽거나(복사하거나) 변경
간접참조: 변수의 주소를 통해 자료값을 복사하거나 변경
int exp_a;
int * exp_a_p; (int* exp_a_p;, int *exp_a_p;)
exp_a = 12;
exp_a_p == &exp_a;
*exp_a_p == *&exp_a == exp_a == 12;
변수 주소 - 주소 연산한(주소 연산자&를 붙인) 변수명, &exp_a
1. 자료값(내용물) - 변수에 대입된 자료
2. 연산 결과 - (자료가 담긴) 해당 변수의 (기억공간의) 주소
3. 연산 목적 - 주소를 통해 해당 자료형의 공간 표시
포인터 exp_a_p - 간접참조하기 위한 변수의 주소를 저장한 변수
1. 자료값(내용물) - 가리키는(지시, 지적하는, pointing) 해당 자료형 자료의 시작 주소, 첫주소
2. 목적 - 가리킨(지시한, 지적한, pointing) 주소를 통해 기억공간(대상, 목적물)을 간접참조하여 자료를 복사하거나 변경
3. 조건 - 간접참조가 목적이므로 지적한 변수와 자료형이 일치 필요
가르킴, 지시, 지적, pointing = 주소, 주소값 저장(보유)
가르키는 것, 지적 대상은 목적에 따라 자료, 자료값이기도 하고 공간이기도 함
1. 지적된 해당 자료형의 변수에 저장된 자료값을 참조(복사)하려면 실행문 우변에 위치(r value)하여 참조 연산자 별을 붙여 지적하는 주소에(를 통해) 접근하여 자료값을 간접참조(복사)
2. 지적된 해당 자료형의 변수의 공간에 접근하여 자료를 참조(변경, 재할당?)하려면 실행문 좌변에 위치(l value)하여 참조 연산자 별을 붙여 지적하는 주소인(주소를 통해) 공간에 접근하여 자료값을 간접참조(변경)
대상에 접근하여 참조(사용)하기 위해 참조 연산자 별 표시
역할 구분
int * pa = &a; 선언시 선언문에 변수 a의 주소를 포인터 변수 pa에 저장
포인터 변수 pa가 a의 주소 &a를 저장하고 있으므로 pa == &a
*pa는 a의 주소 &a를 통해 간접참조한 a의 자료값이므로 *pa == *&a == a
&pa는 포인터 변수 pa 자체의 주소, 다중 포인터에서 사용 scanf에서 입력받아 변수 a에 직접 저장할 수 없어 그 주소 &a를 통해 넣음
scanf에서 입력받아 변수 a에 직접 저장할 수 없어 그 주소 &a를 통해 넣음
배열명 - 배열의 첫주소인 상수(첫주소값이므로 포인터 변수, 주의: 배열이 아니라 배열명)
32비트 CPU에서의 포인터 예
왼쪽 코드를 x386에서 실행했을 때의 출력 결과 예제 및 도식화
&a[0] = 0x0040DF04 &pa = 0x0040DF00 pa = 0x0040DF04 fun = 0x00401060
int a[10];
int *pa;
void fun(int cnt);
void printvar()
{
printf("&a[0] = 0x%08X\n", &a[0]);
printf("&pa = 0x%08X\n", &pa);
printf("pa = 0x%08X\n", pa);
printf("fun= 0x%08X\n", (int) fun);
return;
}
void fun(int cnt)
{
pa = a;
while (cnt) {
*pa = cnt;
pa++;
cnt--;
}
}
변수 int *pa;는 특정 데이터 영역의 위치 정보를 가지고 데이터를 액세스하는 것이다. 따라서 실제로 정수형 데이터가 들어가는 것이 아니고, 정수형 데이터가 들어갈 주소값이 저장된다.
할당 및 해제
C언어에서 변수는 정적변수와 동적변수가 있다. 처리할 데이터의 숫자를 예측할 수 있다면 정적으로 선언하면 된다. 배열변수들이 예이다. 그러나 데이터 처리를 미리 예측할 수 없다면 최대의 데이터 처리량을 정하고 정적으로 선언할 수 밖에 없다. 이 문제를 해결하기 위해 동적 변수를 사용한다.
C에서는 전통적으로 동적할당 변수를 잡을 경우 malloc() 함수를 사용하여 데이터 저장공간을 확보한다. 이와 관련된 함수는 다음과 같다.
- 할당 : malloc() : 힙영역으로부터 데이터 공간을 할당 받는다.
- 재할당 : realloc(): 이미 할당된 메모리 공간의 크기를 조정한다.
- 해제 : free() : 더 이상 쓸 필요가 없는 메모리 공간을 힙영역에 반환한다.
그리고 사용이 완료되면 free() 함수로 해제한다. 해제된 메모리 영역은 다시 재활용된다. 프로세서의 메모리 맵에서 힙(heap) 영역을 활용하여 동적 저장 공간을 확보한다. 각 프로세스마다 힙 영역은 크기가 정해져 있기 때문에 무한정 malloc() 함수를 사용하여 동적으로 잡을 수 없다. 따라서 개발자는 free() 함수로 해제해 주어야 한다. 만약 동적 할당을 하지 못하면 주소값이 NULL로 반환된다.
int main(int argc, char *argv[])
{
int *pa = NULL;
// ...
pa = (int *) malloc( sizeof(int));
if (pa == NULL) { // malloc()가 NULL 리턴하면 확보 실패.
printf("저장공간 확보 실패.\n");
return -1;
}
*pa = 10;
printf("&pa = 0x%08X\n", &pa);
printf("pa = 0x%08X - 내용은 %d\n", pa, *pa);
// ...
if (pa) free(pa); // 사용 후 할당 영역을 반납한다.
return 0;
}
C++에서는 new와 delete가 추가 되었다. 이것은 내부에서 malloc() 함수 기능을 하는 기능을 수행한다. 따라서 표현법이 확장되었고, 힙영역을 활용하는 것은 같다.
포인터 연산
포인터의 실제 형식은 시스템 종속 정수형이다. 즉, 32비트 플랫폼에서 포인터는 4바이트 정수이며, 64비트 플랫폼에서 포인터는 8바이트 정수형이다. 정수이기 때문에 간단한 덧셈 연산이 유효하다.
- 포인터의 덧셈 : 기준 포인터로부터 n바이트 더해진 주소의 메모리를 참조한다.
운영 체제에서 할당하는 동적 공간
프로세서의 힙공간은 제한적이다. MFC와 같은 윈도 프로그램의 경우, 필요에 따라 운영 체제의 자원을 할당 받아 동적공간을 확보할 수 있다. 이와 관련된 함수들이 존재한다.
이것 역시 동적 할당이기 때문에 포인터 변수를 사용하여 처리한다. 프로세서에서 할당 받은 공간은 실행이 끝날 때까지 동적으로 유지되지만, 이와 관련된 할당은 운영 체제와 관련 되어 있으므로 메모리 공간을 잠금을 할 경우도 있다.
- 할당 : 운영 체제로부터 메모리 공간 일부를 빌린다. 정적 메모리 할당과 동적 메모리 할당의 2가지 종류가 있다.
- 재할당 : 이미 할당된 메모리 공간의 크기를 조정한다.
- 해제 : 더 이상 쓸 필요가 없는 메모리 공간을 운영 체제에 반환한다.
이 작업이 제대로 이루어지지 않을 경우 운영체제는 리소스 부족, 알 수 없는 오류, 잘못된 연산 수행과 같은 오류를 일으킨다.
같이 보기
외부 링크
- (영어) Ritchie, Dennis M. The Development of the C Language보관됨 2014-10-18 - 웨이백 머신. History of Programming Languages-II. Second History of Programming Languages conference, Cambridge, Massachusetts, April, 1993. ISBN 0-201-89502-1.
- Richard M. Stallman: Using and Porting the GNU Compiler Collection, Free Software Foundation, ISBN 0-595-10035-X
- Richard M. Stallman: Using Gcc: The Gnu Compiler Collection Reference, Free Software Foundation, ISBN 1-882114-39-6
- Brian J. Gough: An Introduction to GCC, Network Theory Ltd., ISBN 0-9541617-9-3
모듈:Authority_control 159번째 줄에서 Lua 오류: attempt to index field 'wikibase' (a nil value).
- 스크립트 오류가 있는 문서
- 출처가 필요한 글/2010년 11월
- 영어 표기를 포함한 문서
- 잘못된 파일 링크가 포함된 문서
- 웹아카이브 틀 웨이백 링크
- 위키데이터 속성 P18을 사용하는 문서
- 위키데이터 속성 P41을 사용하는 문서
- 위키데이터 속성 P94를 사용하는 문서
- 위키데이터 속성 P117을 사용하는 문서
- 위키데이터 속성 P154를 사용하는 문서
- 위키데이터 속성 P213을 사용하는 문서
- 위키데이터 속성 P227을 사용하는 문서
- 위키데이터 속성 P242를 사용하는 문서
- 위키데이터 속성 P244를 사용하는 문서
- 위키데이터 속성 P245를 사용하는 문서
- 위키데이터 속성 P268을 사용하는 문서
- 위키데이터 속성 P269를 사용하는 문서
- 위키데이터 속성 P271을 사용하는 문서
- 위키데이터 속성 P347을 사용하는 문서
- 위키데이터 속성 P349를 사용하는 문서
- 위키데이터 속성 P350을 사용하는 문서
- 위키데이터 속성 P373을 사용하는 문서
- 위키데이터 속성 P380을 사용하는 문서
- 위키데이터 속성 P396을 사용하는 문서
- 위키데이터 속성 P409를 사용하는 문서
- 위키데이터 속성 P428을 사용하는 문서
- 위키데이터 속성 P434를 사용하는 문서
- 위키데이터 속성 P435를 사용하는 문서
- 위키데이터 속성 P436을 사용하는 문서
- 위키데이터 속성 P454를 사용하는 문서
- 위키데이터 속성 P496을 사용하는 문서
- 위키데이터 속성 P549를 사용하는 문서
- 위키데이터 속성 P650을 사용하는 문서
- 위키데이터 속성 P651을 사용하는 문서
- 위키데이터 속성 P691을 사용하는 문서
- 위키데이터 속성 P716을 사용하는 문서
- 위키데이터 속성 P781을 사용하는 문서
- 위키데이터 속성 P791을 사용하는 문서
- 위키데이터 속성 P864를 사용하는 문서
- 위키데이터 속성 P865를 사용하는 문서
- 위키데이터 속성 P886을 사용하는 문서
- 위키데이터 속성 P902를 사용하는 문서
- 위키데이터 속성 P906을 사용하는 문서
- 위키데이터 속성 P947을 사용하는 문서
- 위키데이터 속성 P950을 사용하는 문서
- 위키데이터 속성 P966을 사용하는 문서
- 위키데이터 속성 P982를 사용하는 문서
- 위키데이터 속성 P1003을 사용하는 문서
- 위키데이터 속성 P1004를 사용하는 문서
- 위키데이터 속성 P1005를 사용하는 문서
- 위키데이터 속성 P1006을 사용하는 문서
- 위키데이터 속성 P1015를 사용하는 문서
- 위키데이터 속성 P1045를 사용하는 문서
- 위키데이터 속성 P1048을 사용하는 문서
- 위키데이터 속성 P1053을 사용하는 문서
- 위키데이터 속성 P1146을 사용하는 문서
- 위키데이터 속성 P1153을 사용하는 문서
- 위키데이터 속성 P1157을 사용하는 문서
- 위키데이터 속성 P1186을 사용하는 문서
- 위키데이터 속성 P1225를 사용하는 문서
- 위키데이터 속성 P1248을 사용하는 문서
- 위키데이터 속성 P1273을 사용하는 문서
- 위키데이터 속성 P1315를 사용하는 문서
- 위키데이터 속성 P1323을 사용하는 문서
- 위키데이터 속성 P1330을 사용하는 문서
- 위키데이터 속성 P1362를 사용하는 문서
- 위키데이터 속성 P1368을 사용하는 문서
- 위키데이터 속성 P1375를 사용하는 문서
- 위키데이터 속성 P1407을 사용하는 문서
- 위키데이터 속성 P1556을 사용하는 문서
- 위키데이터 속성 P1584를 사용하는 문서
- 위키데이터 속성 P1695를 사용하는 문서
- 위키데이터 속성 P1707을 사용하는 문서
- 위키데이터 속성 P1736을 사용하는 문서
- 위키데이터 속성 P1886을 사용하는 문서
- 위키데이터 속성 P1890을 사용하는 문서
- 위키데이터 속성 P1907을 사용하는 문서
- 위키데이터 속성 P1908을 사용하는 문서
- 위키데이터 속성 P1960을 사용하는 문서
- 위키데이터 속성 P1986을 사용하는 문서
- 위키데이터 속성 P2041을 사용하는 문서
- 위키데이터 속성 P2163을 사용하는 문서
- 위키데이터 속성 P2174를 사용하는 문서
- 위키데이터 속성 P2268을 사용하는 문서
- 위키데이터 속성 P2349를 사용하는 문서
- 위키데이터 속성 P2418을 사용하는 문서
- 위키데이터 속성 P2456을 사용하는 문서
- 위키데이터 속성 P2484를 사용하는 문서
- 위키데이터 속성 P2558을 사용하는 문서
- 위키데이터 속성 P2750을 사용하는 문서
- 위키데이터 속성 P2980을 사용하는 문서
- 위키데이터 속성 P3223을 사용하는 문서
- 위키데이터 속성 P3233을 사용하는 문서
- 위키데이터 속성 P3348을 사용하는 문서
- 위키데이터 속성 P3372를 사용하는 문서
- 위키데이터 속성 P3407을 사용하는 문서
- 위키데이터 속성 P3430을 사용하는 문서
- 위키데이터 속성 P3544를 사용하는 문서
- 위키데이터 속성 P3562를 사용하는 문서
- 위키데이터 속성 P3563을 사용하는 문서
- 위키데이터 속성 P3601을 사용하는 문서
- 위키데이터 속성 P3723을 사용하는 문서
- 위키데이터 속성 P3788을 사용하는 문서
- 위키데이터 속성 P3829를 사용하는 문서
- 위키데이터 속성 P3863을 사용하는 문서
- 위키데이터 속성 P3920을 사용하는 문서
- 위키데이터 속성 P3993을 사용하는 문서
- 위키데이터 속성 P4038을 사용하는 문서
- 위키데이터 속성 P4055를 사용하는 문서
- 위키데이터 속성 P4114를 사용하는 문서
- 위키데이터 속성 P4143을 사용하는 문서
- 위키데이터 속성 P4186을 사용하는 문서
- 위키데이터 속성 P4423을 사용하는 문서
- 위키데이터 속성 P4457을 사용하는 문서
- 위키데이터 속성 P4534를 사용하는 문서
- 위키데이터 속성 P4535를 사용하는 문서
- 위키데이터 속성 P4581을 사용하는 문서
- 위키데이터 속성 P4613을 사용하는 문서
- 위키데이터 속성 P4955를 사용하는 문서
- 위키데이터 속성 P5034를 사용하는 문서
- 위키데이터 속성 P5226을 사용하는 문서
- 위키데이터 속성 P5288을 사용하는 문서
- 위키데이터 속성 P5302를 사용하는 문서
- 위키데이터 속성 P5321을 사용하는 문서
- 위키데이터 속성 P5368을 사용하는 문서
- 위키데이터 속성 P5504를 사용하는 문서
- 위키데이터 속성 P5587을 사용하는 문서
- 위키데이터 속성 P5736을 사용하는 문서
- 위키데이터 속성 P5818을 사용하는 문서
- 위키데이터 속성 P6213을 사용하는 문서
- 위키데이터 속성 P6734를 사용하는 문서
- 위키데이터 속성 P6792를 사용하는 문서
- 위키데이터 속성 P6804를 사용하는 문서
- 위키데이터 속성 P6829를 사용하는 문서
- 위키데이터 속성 P7293을 사용하는 문서
- 위키데이터 속성 P7303을 사용하는 문서
- 위키데이터 속성 P7314를 사용하는 문서
- 위키데이터 속성 P7902를 사용하는 문서
- 위키데이터 속성 P8034를 사용하는 문서
- 위키데이터 속성 P8189를 사용하는 문서
- 위키데이터 속성 P8381을 사용하는 문서
- 위키데이터 속성 P8671을 사용하는 문서
- 위키데이터 속성 P8980을 사용하는 문서
- 위키데이터 속성 P9070을 사용하는 문서
- 위키데이터 속성 P9692를 사용하는 문서
- 위키데이터 속성 P9725를 사용하는 문서
- 위키데이터 속성 P9984를 사용하는 문서
- 위키데이터 속성 P10020을 사용하는 문서
- 위키데이터 속성 P10299를 사용하는 문서
- 위키데이터 속성 P10608을 사용하는 문서
- 위키데이터 속성 P10832를 사용하는 문서
- 위키데이터 속성 P11249를 사용하는 문서
- 위키데이터 속성 P11646을 사용하는 문서
- 위키데이터 속성 P11729를 사용하는 문서
- 위키데이터 속성 P12204를 사용하는 문서
- 위키데이터 속성 P12362를 사용하는 문서
- 위키데이터 속성 P12754를 사용하는 문서
- 위키데이터 속성 P13049를 사용하는 문서
- 자료형
- 원시 자료형
- 미국의 발명품