'주의점'에 해당되는 글 1건

  1. 2015.05.18 C/C++ 포인터 사용시 주의할 점

6.6 포인터 사용시 유의점     [gdoc]     [smts]

 C 프로그래밍에서 포인터를 사용할 때 주의하지 않으면 치명적인 오류를 발생하게 된다. C프로그래밍이 어렵다는 인식이 있는 것은 바로 포인터를 적절하게 사용하기가 상당히 어렵다는 점에서 기인한다. 포인터는 곧 ‘주소’이므로 어느 주소값을 가르키고 있는가가 가장 중요하다. 먼저 확인할 사항은 포인터가 초기화가 되었는가이다.


 초기화되지 않은 포인터가 있을 경우 컴파일러에 따라 경고 메시지를 내기도 하고 경고 없이 실행하다가 실행 중 오류(runtime error)를 발생시켜서 프로그램이 다운될 수도 있다. 포인터를 사용할 때는 반드시 초기화를 시켜야 한다는 것을 알아두자.


다음 예를 보자


0604-01.c
#include <stdio.h>
int main()
{
double *dpa;
*dpa = 1.0;  //(1)
}

이 예는 언듯 문제가 없어보일 지도 모르지만 포인터 dpa가 초기화되지 않고 (1)에서 사용되었다. 이 프로그램은 컴파일러에 따라 컴파일시 오류를 발생하거나 실행이 되더라도 정상적인 동작을 하지 않고 프로그램이 죽어버릴 것이다. 그 이유는 포인터가 초기화되지 않은 상태이므로 정상적인 주소값을 가지고 있기 때문이다. 비정상적인 주소에 실수값 1.0을 대입하였으니 프로그램이 죽어버리는 것이다.


 포인터를 초기화시키려면 다음과 같이 기존 변수의 주소값을 사용하는 방법이 있다.


0604-02.c
#include <stdio.h>
int main()
{
  double da = 0.0;
  double *dpa = &da; // 변수 da의 주소로 포인터 dpa를 초기화
  *dpa = 1.0;
}

아니면 malloc()함수를 사용하여 정상적인 메모리 공간에  새로운 저장 공간(메모리)과 주소를 할당받을 수 있다.


#include <stdio.h>
int main()
{
  double *dpa = malloc(sizeof(double)); //새로운 저장공간과 주소값 할당
  *dpa = 10;
  …
  free(dpa); // 다 사용한 후 반드시 저장공간을 반환해야 한다.
}

malloc()함수와 free()함수는 stdio.h 헤더파일에 정의된 표준 함수이며 각각 메모리를 할당하고 반환하는 역할을 한다. sizeof()함수는 입력 인자의 바이트수를 반환하는데 sizeof(double) 은 double형의 바이트 수를 반환한다. (이 경우는 8) 따라서 8바이트 저장 공간을 확보한 후 그 주소를 반환한다. 그 주소는 포인터 dpa에 저장된다. 단, malloc()함수로 할당받은 저장 공간은 반드시 free()함수로 반환시켜야 한다.


다음 예는 소위 dangling 포인터의 예이다.


0604-04.c
#include <stdio.h>

int main()
{
int ia=10, *ipa = &ia;
printf("*ipa = %d\n", *ipa);
if (ia==10) {
int ib = 20;
ipa = &ib;
}
printf("*ipa = %d\n", *ipa);
}

if 블록 안에서 ipa가 &ib로 초기화가 되었지만 이 블록에서 벗어나는 순간 내부 변수 ib는 소멸되므로 ipa포인터도 그 소재가 불분명하게 된다. 이러한 경우를 dangling 포인터라고 하는데 프로그램이 길어질 경우 프로그래머가 의식하지 못하는 부분에서 이러한 실수가 일어날 수도 있게 된다. 당연히 이러한 경우도 방지해야 한다.


 다음 예를 보자. 다음 예는 ①에서 func(ipa)라고 호출을 하고 있다. 이 함수 내에서 포인터는 ②에서 내부 지역변수 ia의 주소값으로 초기화 된다. 그런데 func()함수 내부의 ipb포인터는 main()함수의 ipa와는 별개의 포인터이다. 따라서 main()함수 안의 ipa는 여전히 초기화가 되지 않은 포인터이다. 따라서 엉뚱한 값이 나오거나 프로그램이 실행 중지되기도 한다.


0604-03.c
#include <stdio.h>
void func(int*);

int main()
{
int *ipa;
func(ipa); //①
printf("*ipa = %d\n in main()", *ipa);
}

void func(int *ipb)
{
int ia = 20;
ipb = &ia; //②
printf("*ipb = %d in func().\n", *ipb);
}

 이와 같이 포인터를 사용할 경우에는 메모리의 할당과 해제에 각별한 주의를 기울여야 하는데 메모리 누수(leakage)와 같은 예기치 않은 동작을 발생시키기 쉽기 때문이다.



Posted by 살레시오

댓글을 달아 주세요