Lecture 4 of Introduction to Computer science

Lecture 4

이번 강의에서는 지난 주에 이어 메모리를 효율적으로 사용하는 내용을 더 깊게 pointer와 연결하여 공부하였다.




메모리에 데이터를 포인터를 이용해 저장하고 활용하는 방법을 진행하였다. 위와 같은 코드에 x[10] = 50; 이라는 내용을 추가하여 진행하면 제대로 수행되는 것을 확인 할 수 있다. 10개의 공간만 할당했는데 11번째 장소에 저장해도 동작하는 이유는 이전에 말했던 것과 마찬가지로 할당하고자하는 공간보다 더 많은 자리를 차지하기 때문이다.


 이번에는 Valgrind라는 명령어를 통해 위의 코드가 어떤 식으로 동작하고 있는지 보여주는 모습이다.
 초반부에는 메모리 주소를 보여주며 어떻게 할당되어 있는지 보여준다. 각 코드의 어떤 라인이 영향을 끼치는지도.


이번에는 help50를 통해 어떤식으로 조언을 받을 수 있는지 명령을 입력하면,


위와 같이 어떤 부분이 잘못되었는지 알려주며 이 내용은 할당되지 않은 영역을 침범했다는 것이다. (11번째 장소에 데이터를 저장하였기 때문에)


그래서 11번째위치가 아닌 10번째 위치로 변경하여 다시 진행하면,


이번에 확인할 수 있는 내용은 10번째 자리에 데이터가 제대로 들어섰지만 대신 대부분의 메모리가 새고(memory leak) 있다는 것을 알 수 있다.


따라서 free 함수를 통해 위와 같이 메모리 할당에 대해 조정을 해주면 해결이 되는 것을 알 수 있다. ddb50라는 debugger에 대해서도 설명을 해주는데 타인의 코드를 이해하는데 시간을 굉장히 절약해 줄 수 있다고 한다.


이전에 말했던 메모리구조에서 heap에서는 dynamic memory가 차지하며 stack에서는 변수나 함수들이 차지한다고 했었다. 또한 위의 예시에서 pointer가 없다면 일어나는 일들을 다시 한번 되짚는다.


Binky라는 애니메이션을 통해서 pointer가 작동하는 프로세스에 대해 다시 한번 복습하고,


y에 값이 들어서지 못하는 이유까지 다시 한번 설명을 들었다. 그러면서 할당되지 않은(not dereferencing) pointer에 값을 집어 넣으려고 하면 메모리 침범(invade)에 의해 부작동(malfunction)이 일어 날 수 있음을 다시 한번 상기시켰다.


여기서는 사용자정의의 타입을 만들 때 방법에 대해 설명한다. 이를 struct라고 하며 정의할 때는 typedef를 통해 생성할 수 있다.


위에서 만든 struct를 기반으로 students라는 struct를 만들어 활용하는 모습이다. students안에 있는 name과 dorm에 접근하려면 students.name과 같은 형태로 기입하는 것을 확인 할 수 있다.


다시 array로 돌아와서 array의 특징은 정보를 저장할 때 데이터타입은 한가지로 제한된다. 두가지 이상의 타입을 섞을 수 없다. 또다른 특징은 한번 할당했을 때 크기를 변경할 수 없다는 것이다. 따라서 위에서 학생들의 정보를 담을 때 일정 수준의 크기의 데이터는 담을 수 있더라도 어느 순간 일정량을 초과하면 이 것에 대한 재조정이 필요하다. 여기서 해결방안을 제시하는데, (realloc이라는 함수도 등장하지만 결국 언젠가 재조정이 필요한 상황)


위와 같이 데이터 구조가 얼마나 크던지 각 데이터들이 다음 정보에 대해 pointer형식으로 가리키게 되면 해결된다. 즉 본인의 데이터와 다음 데이터의 주소를 들고 있는 형태가 될텐데 다음과 같이 정리할 수 있겠다. (마지막 데이터의 다음 주소값은 null값을 가지게 된다)


새로운 struct로 node라는 구조를 정의하고 n이라는 변수에 값을 저장하게 되고 다음 데이터의 주소값을 가지면 된다. 이때 데이터 타입은 pointer를 활용한 것이 되어야 다음 데이터를 가리킬 수 있기 때문에,


다음 node의 구조의 주소를 pointer로써 향하게 된다. (next다음 데이터의 주소가 된다)


이번에도 쉬운 설명을 위해 학생들을 동원하여 어떻게 구조에서 pointer가 역할을 수행하는 지 보여준다. 기존에 본인이 들고있는 숫자(value)의 오름차순으로 손가락으로 가리키게 하고(pointer) 여기에 추가적으로 학생들(value)이 늘어나면


다시 손가락의 방향들을 바꿔 다시금 오름차순을 맞추는 순서를 보여준다. 이때 주의할 점은 pointer로 연결되어 있는 구조를 먼저 끊고 다시 연결하지 않고 새로들어온 데이터가 먼저 연결이 되고 다시금 순서를 맞추는 방법으로 진행하였다. 그 이유는 연결을 끊어버려서 다음 주소값을 바꿔버리면 그 값을 잃게 되어 다음 값이 어디를 가리켜야 할 지 알 수 없기 때문이다. (temp에 값을 임시로 저장한 것과 같이 진행해야 하는 순서)


위의 list0.c에서는 데이터 양에 따라 숫자를 입력받고 이를 그대로 출력해주는 프로그램이다. 하지만 데이터량(capacity)는 이미 정해져 있고 그에 따라 횟수가 정해져 있어 아래와 같이 더 유용하게 변경하였다.


capacity를 입력 받아 입력하고 싶은 만큼의 데이터를 받을 수 있게 하고 do-while문으로 변경하여 값이 0이상일 때만 작동하도록 수정하여 더 효과적인 코드로 탈바꿈하였다.


다음 list1.c에서는 numbers를 pointer를 지정해놓고 입력이 되는 값에 따라 메모리를 realloc으로 재할당하도록 꾸몄다. 이를 통해 null값 대신 입력이 되는 값에 따라 메모리를 더 잡아줌으로 메모리를 효율적으로 쓸 수 있게 된다.


프로그램은 강제종료하고 싶다면 Ctrl + C이지만 파일을 마무리(End of File)하고 싶다면 Ctrl + D를 통해 끝낼 수 있다는 점을 확인하였다. 이 설명은 위의 코드만으로 종료할 수 없기 때문에 질문을 끝내고 파일 자체에서 마무리를 지을 수 있는 방법에 대해 얘기가 나왔었다.


다시 코드로 돌아와서 입력한 데이터들에 대해 저장하고 끝나면 값들을 출력하는 내용이며 더이상 입력하고 싶지 않을 때 ctrl + D를 입력하면 위와 같이 출력값들을 내보내고 프로그램이 정상종료되는 것을 확인할 수 있다.


이번엔 해당 코드를 valgrind를 통해 분석을 해보았다. heap summary에 보면 12바이트나 메모리 할당을 해놓은 것을 볼 수 있고 이는 3개의 값이 저장되었기 때문에 각각 4바이트로 계산하여 저장된 것이며 이 이후에 free함수를 활용해주면 memory leak가 0바이트로 수렴한 것을 알 수 있다.


마지막 예시로 list2.c를 보여주며 대동소이하지만 위에서 설명한 것과 같이 node를 통해 데이터들을 pointer로 연결하려고 한다. 위에 강조된 부분은 전에 나왔던 내용과 같이 n에 null값이 들어서 있을 때 오류임을 알려주는 것이다.


밑에 n -> number 와 같이 표기 된것은 위와 같이 *n.number에 대한 다른 표기법이며 구조안의 값들에 입력된 값을 할당 하는 것을 확인 할 수 있다.


따라서 number에서는 값을 저장하며 이어 next에서는 다음 데이터의 주소값이 들어선다.


40줄의 if (number)는 if (number != null)과 동일한 내용이며 42줄의 for문은 초기식에 ptr이라는 node에 값을 입력하고 조건식으로 null이 아닌지 확인하며 증가식에는 ptr을 next node가리키는 구조를 가지고 있다.


ptr이 next를 가리키고 그 값이 null이라면 n의 node에 pointer로 가리키게 하고(마지막 값이 아니게 되었기 때문에)for문을 빠져나가게  된다.


위의 내용을 도식화하면 위와 같고 ptr가 계속 다음, 다음 node로 이동하며(주소값이 변경되는 것)null값을 찾아 마지막 값을 pointer로 연결하는 모습이다.


 이번에는 학생들의 이름을 구조화 했을 때 효율적으로 다룰 수 있는 방법에 대해 얘기하는 장면이며 이 때 이름들을 알파벳에 따라 분류하면 26가지 구조에 대해 관리하기 용이할 것이며 이를 hash function이라고 칭한다. hash function이 나타내는 정보들을 hash table이라고 하며 여기에 학생들의 이름이 저장되는 것이다.
 더 효과적인 방법으로 더 많은 분류자(bucket)을 만든다고 하는 것인데 AA, AB, AC...과 같은 방법이나 학생ID, 학년 등과 같은 기준이 적용될 수 있다고 한다.


언급했던 26개의 알파벳으로 분류하는 것은 위와 같은 트리구조로 표현될 수 있는데,


위와 같이 각 알파벳을 맞춰나가면 트리구조를 형성하는 방식이다. Billy라는 이름을 검색할 때 첫글자인 B를 찾고 다음은 i, 다음은 l... 마지막으로 y를 찾아 완성하는 형식이다.


같은 내용을 도식화한 모습이다. Billy를 찾기위해 필요한 공간은 26 * 5만큼이라서 굉장히 비효율적으로 보일 수 있겠지만 다른 이름들을 찾을 수 있기 때문에 이런 방법이 오히려 더 효율적으로 움직일 수 있음을 시사한다.


댓글