Skip to content
 

포인터와 어드레스

C 언어를 공부하다보면 가장 많이 나오는 단어가 바로 포인터(*) 와 어드레스(&) 일거다.
이 두차이의 의미를 정확히 이해한다면 C 언어의 50% 정도는 이해했다고 해도 무방하다.

간단히 두 단어의 뜻을 정의한다면 포인트는 메모리주소를 담는 주소값이고 어드리세는 할당된 메모리주소를 담는 주소값이다.
어드레스는 변수 또는 함수의 절대 주소이며 그 값은 변동이 없다는 뜻이다. 하지만 포인트는 그 어드레스를 값을 담아서 변동하는 주소라고 생각하면 된다.

char *ptr = “1234567890”;
char str[10] = “abcd”;

이 두줄에서 나온 포인터와 어드레스는 정확히 몇개일까요?
이렇게 질문하면 아마도 포인터 변수 하나에 char 버퍼가 하나인데요라고 답을 할것이다. 꼭 틀린답은 아니지만 정확하다고 할 순 없다.
여기서 포인트는 1개 어드레스 2개가 정답이다.

“1234”, “abcd” 이 두개가 바로 어드레스 2개이다.
프로그램상에서 두 스트링 열이 스택상에 그 내용을 담고서 그 번지를 지정하고 있다. ptr 변수는 바로 “1234” 의 주소값을 담고 있는 변수이고 str 은 “abcd”로 초기화된 어드레스의 주소이다.

많은 프로그램어들이 자주 실수하는 버그 유형중에 하나가 포인터 변수의 사이즈를 잘목계산해서 발생하는 로직 오류다.
sizeof( ptr );
sizeof( str );
이두 줄의 결과는 4 와 10 이다. 여기서 sizeof의 의미는 해당 변수의 할당된 영역의 크기를 반환하는 함수다. ptr의 size는 어드리스(4바이트) 이므로 4이고 str의 size는 10개의 char 의 방을 잡고 있으므로 10인것이다.

char *a, int *b, long *c 이 세가지 변수의 sizeof의 결과는 모두 4를 반환하는 이유이다.

버퍼를 초기화 하는 방법에 memset을 많이들 사용한다.
memset( ptr, 0, sizeof(ptr) );
memset( str, 0, sizeof(str) );
이렇게 사용하게 되면 첫번째는 4바이트만 초기화가 된다.

포인터 변수가 혼동되는 이유중에 하나가 바로 표기상의 문제다.
char *p, char* p, char * p
띄어쓰기의 차이는 있지만 3가지 모두 같은 의미로 컴파일된다.
가장 정확한 표기는 char * p => (char *)p 이렇게 생각하는게 쉽다
즉 p는 char * 형태의 변수를 의미한다는것이다.
모든 포인트 변수는 4바이트의 주소값(프로세서에 따라서 가변)을 지정하기 때문에 모두 같은 사이즈가 된다.

char *a, int *b, long *c 의 주소가 모두 4바이트라면 타입의 의미는 어떻게 사용될까?
char는 1바이트, int는 2바이트, long은 4바이트라는 가정을 한다면
a = 100; b = 100; c = 100;

각 변수를 하나씩 증가하면 a++, b++, c++ 그때 값은 얼마일까?
a = 101; b = 102; c = 104; 이렇게 변해있을것이다.

바로 어드레스 증가시 사용될 offset을 의미한다.

숫자에서 가장 중요한 값은 바로 0 이다.
이와 대등한 수준에서의 변수 형태가 바로 void이다.
포인트 다음으로 아마도 자주 사용하면서도 정확히 이해가 어려운 타입이기도 하다. void 를 설명하자면 무형식의 형식이다.

void *a 라고 선언을 하게 되면 a 는 바로 어드레스를 담을 수 있는 변수는 되지만 a++ 를 할 수가 없다. 컴파일러가 얼마를 이동해야 할지 모르기 떄문이다. 하지만 모든 포이터 변수를 받아들이기 위한 값이 되기 때문에 아주 중요하게 이용되기도 한다.

함수가 변수 전달이나 함수를 전달하는 방식에서도 포인트와 어드레스는 다르게 사용된다.
함사를 호출하는건 해당 함수의 어드레스를 호출하는것이며 이때 변수를 넘겨주기 위해서 변수의 값 또는 그 변수의 주소를 직접 넘겨주는 방식으로 함수를 호출 할수 있다.

fun( int a ), fun( int *a ), fun( int &a ) 이렇게 3개지 형태로 함수를 정의 할 수 있다. 앞의 두가지가 C에서 사용하는 형식이고 3번째는 C++의 Call by Reference의 방법이라 약간은 다른 내용이다.

이 함수를 부는 방식은 fun( a ), fun( &a ), fun( a )이런식으로 각각 호출을 하게 될것이다.

첫번쨰는 함수 호출시 그 값을 넘겨주는것이면 두번째는 변수의 주소를 넘겨주는것이며 세번째는 복재의 의미로 사용되는 형태이다.

두번째 방식이 중요한 포인터의 내용이 된다.

int fun_a( int a ) { return a+1; }
void fun_b( int *a ) { *a++; }

void main()
{
int i = 10;
i = fun_a( i );
fun_b( &i );
}

두개의 함수는 거의 비슷한 결론을 만들어 내지만 그 호출 방법과 연산 방법이 상당히 다른걸 볼 수 있다.

프로그램에 있어서 포인트만을 가지고 코딩을 하고 1,2,3,4 차원의 배열과 포인트만으로 아주 복잡한 코딩을 고집할 필요는 없다. 하지만 포인트 자체를 무시하고 모든 함수를 작성하다보면 글로벌 변수가 많이 늘어나거나 함수의 인자가 복잡해 지는 결과가 나오게 된다.

정확한 이해를 통한 포인트의 사용많이 간결하면서도 효율적인 포인트 프로그램이 될것이다.

다음번에는 왜 자바엔 포인트가 없을까에 대해서 정리를 해볼까 한다.

Leave a Reply