segmentation fault의 원인

segmentation fault는 잘못된 메모리 참조 때문에 발생한다. 즉, 건드리지 말아야 할 곳을 건드렸기 때문에 발생하는 에러이다. 주로 NULL 로 설정된 영역1을 건드리거나, 할당받은 메모리 공간을 넘은 곳을 건드렸을 때 발생한다. 난해한 점은 디버그 모드로 프로그램을 실행시킬 경우, 건드리지 말아야 할 메모리를 건드렸을 때가 아니라, 할당받은 메모리를 해제할 때 에러가 발생한다는 점인데, 예를 들면 다음과 같다.

int *p = new int[5];
p[5] = 11; // 여기서는 에러가 발생하지 않는다. 
delete p;  // 여기서 에러 발생. 

gcc(나 g++)의 경우는 컴파일 할 때 option 을 지정해 주지 않으면 release mode로 컴파일이 되기 때문에 잘못된 메모리를 건드릴 때 에러가 발생한다.

   할당받은 메모리 이외의 공간을 건드릴 수 있다는 것은 C/C++이 포인터를 사용할 수 있기 때문에 발생하게 되는데, 위의 경우처럼 명시적으로 포인터를 사용했을 때 발생하는 에러야 쉽게 잡히는데, 간혹, 예를 들면 STL을 사용할 때처럼 내부적으로 pointer로 구현되어 있는 기능을 사용하다가 발생할 수도 있다. 예를 들면,

std::vector<int> a;
a.push_back(1);
int sum = std::accumulate(a.begin(), a.begin() + 5, 0); // 여기서 에러 발생.

위와 같이 a의 begin 위치에서 5 번째 요소까지의 위치값(이 값이 포인터인데)까지 값을 더하고자 할 경우, 아직 5 번째 요소는 할당되지 않았기 때문에 저 경우 에러가 발생한다. 문제는 위에는 pointer 를 사용하는 것처럼 보이는 곳이 없다는 것. 물론 내부에서 accumulate 가 어떻게 작동하는지 안다면 쉽게 찾아낼 수 있지만 그렇지 않을 경우 조금 혼동될 수도 있다.

   그 이외에도 잘못된 메모리를 건드리는 경우의 예는 수도 없이 많기 때문에 여기서 일일이 나열하기는 힘들다. 중요한 것은 segmentation fault 가 일어났을 때는 에러가 난 곳과 메모리가 할당된 곳 사이에 반드시 건드리지 말아야 할 곳을 건드리는 부분이 존재한다는 점. release로 실행시킬 경우 segmentation fault가 난 곳에서 메모리를 건드릴 경우가 많고, debug 로 실행할 경우는 그렇게 건드릴 때는 에러가 안 나고 그 메모리를 해제할 때 에러가 날 수 있다. 주로 배열의 index를 잘못 넣는 경우가 있다. 예를 들면

int *p = new int[size];
int pos = ...;
...
p[pos] = some_value; // 이 때, pos는 반드시 [0, size-1] 에 속해야 한다. 
delete p; // 여기서 에러.

위와 같은 형식의 코드에서 pos 가 예상치 못하게 음수를 갖거나 size 이상의 값을 갖는 경우가 있었다는 점. 그럴 리가 없을 것이라는 생각 때문에 에러 처리를 하지 않고 넘어 가게 되면 segmentation fault를 피할 수 없는 것이다. 따라서 코드에서 위와 같은 부분을 면밀히 살펴 보아야 한다.


  1. 윈도우즈의 경우, 모든 프로세스는 그 프로세스가 접근할 수 있는 메모리 영역 중 NULL 에 해당하는 영역을 할당받게 된다. 즉, 메모리 맵 중 NULL 에 해당하는 영역이 있게 된다. 32bit 라면 4GB 공간을 사용할 수 있게 된다 하더라도 일정 구간이 '접근 불가능한 공간'으로 tagging 된다고 할 수 있겠지. 보다 자세한 내용은 Jeffrey Richter의 책을 참고. [본문으로]

댓글

가장 많이 본 글