대표적인 메모리 관련 버그
컴퓨터 구조
컴퓨터 구조를 공부하면서 알게된 내용을 요약해서 작성해보자.
지역 변수의 포인터 반환하기
1 2 3 4 5 6 7 8 9 10 int* func() { int a = 2; return &a; } void main() { int* p = func(); *p = 20; }
- 지역 변수 a가 func 함수의 스택 프레임에 위치하며 func 함수의 실행이 끝나면 해당 스택 프레임도 없어진다. main 함수가 func 함수를 호출한 후 얻는 포인터는 이미 없는 변수를 가리키게 된다.
포인터 연산의 잘못된 이해
1 2 3 4 5 6 7 8 9 10 int sum(int* arr, int len) { int sum = 0; for(int i = 0; i < len; i++>) { sum += *arr; arr += sizeof(int); } return sum; }
- 포인터 연산에서 1을 더하는 것은 1바이트만큼 이동하는 것이 아니라 단위 한 개만큼 이동하는 것이다.
- 단위 한 개는 포인터가 가리키는 데이터 형식의 크기에 해당한다.
- 포인터가 가르키는 데이터 형식이 int 형일 때 포인터에 1을 더하는 것은 4바이트 만큼 이동하는 것이다.
문제 있는 포인터 역참조
1 2 int a; scanf("%d", a);
- a 값이 코드 영역이나 기타 읽기 전용 영역을 가리키는 포인터 값으로 해석되면 운영 체제는 이 프로세스를 즉시 강제 종료 시킨다. 이것은 제일 좋은 상황이며, 문제를 찾는 것은 그리 어렵지 않다.
- a 값이 스택 영역을 가리키는 포인터 값으로 해석되었다면 다른 함수의 스택 프레임이 파괴되었기 때문에 프로그램이 이제 무슨 짓을 할 지 알 수 없으며, 이런 버그는 원인을 찾기가 매우 어렵다.
이미 해제된 메모리 참조하기
1 2 3 4 5 6 7 void add() { int* a = (int*)malloc(sizeof(int)); ... free(a); int b = *a; }
- 포인터 a가 가리키는 메모리 조각이 해제된 후 malloc으로 다시 할당하지 않았다면 a가 가리키는 값은 이전과 동일하다.
- 포인터 a가 가리키는 메모리 조각이 이미 malloc로 할당되었다면 a가 가리키는 메모리는 이미 덮어쓰기가 되었을 수 있다. a를 역으로 참조하여 얻는 것은 이미 덮어쓰기가 된 데이터다.
스택 넘침
1 2 3 4 5 6 void buffer_overFlow() { char buf[32]; gets(buf); return; }
- 모든 함수는 실행될 때 스택 영역에 자신만의 스택 프레임을 가진다.
메모리 누수
1 2 3 4 5 void memory_leek() { int *p = (int *)malloc(sizeof(int)); return ; }
- 프로세스가 종료되기 전까지는 다시 해제할 방법이 없어 메모리 누수가 일어난다.