ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [프언] #09. Records & Pointers
    2023-2학기/프로그래밍언어 2023. 11. 20. 15:27

    이번 글에서는 C에서의 Struct와 Pointer와 유사한 기능을 한번 추가해보겠습니다.

     

    Record

    Record는 어떤 데이터가 특정한 형태를 가질 때, 이를 묶어서 편리하게 처리할 수 있도록 만들어진 데이터 형식 중 하나입니다. C의 구조체, 오브젝트 등이 바로 Record입니다.

    먼저 저희가 제작하고 있는 언어에서 Record를 어떻게 정의하고 호출해야 할 지를 정해야 합니다. 이 언어에서 변수를 정의할 때는 let을 사용했었습니다.

    let x = 10 in x + 2

    이와 비슷하게 아래와 같이 Record를 정의하고 호출하는 방법을 생각할 수 있겠네요.

    let student = {id := 20231111, age := 20}
    in student.id + student.age

    Syntax를 조금 정리하면 아래와 같습니다.

    Semantics를 Inference Tree로 나타내면 아래와 같습니다.

     

    Pointer

    다음으로는 포인터를 정의해보겠습니다. 먼저 아래 C의 문법을 사용한 코드를 통해 포인터에 대해 간단히 복습하고 넘어가겠습니다.

    let x = 1
    in let y = &x
    in *y := *y + 2

    먼저 첫번째 줄에서 변수 x의 값을 1로 정의했습니다. 그리고 두번째 줄에서 &x라는 문법이 나옵니다. &x는 x 변수의 주소값을 의미합니다. 즉, 두번째 줄에서 변수 x의 주소값을 변수 y에 지정해준 것입니다.

    세번째 줄에서는 *y라는 문법이 등장합니다. * 기호는 주소를 따라가라는 것을 의미합니다. 즉, *y는 y에 저장된 주소값에 있는 변수를 의미하고, 위 코드에서 이것은 x를 의미합니다. 따라서 세번째 줄은 x의 값을 2 증가시키는 것을 의미합니다.

    비슷한 방법으로, Struct와 Struct의 멤버의 주소 역시 불러올 수 있습니다.

    let tree = { left := {}, val := 1, right := {} }
    in let x = &tree.val
    in *x := *x + 2

    위를 정리하면 아래와 같습니다.

    여기서 엄밀하게 따지자면, *E 문법의 경우는 경우에 따라 의미하는 바가 다릅니다. := 왼쪽에 나타나는 *E는 E가 참조하는 위치를 의미하고, 그 외의 경우에는 E가 참조하는 위치의 값을 의미합니다.

    let tree = { left := {}, val := 1, right := {} }
    in let x = &tree.val
    in *x := *x + 2

    아까 예시로 봤던 이 코드를 보겠습니다. 세번째 줄에서 왼쪽에 있는 *x는 x가 참조하는 위치를 의미하므로 tree 구조체의 val을 의미하며 다음에 나오는 *x는 이 위치에 저장된 값인 1을 의미합니다.

     

    메모리 관리

    이렇게 메모리를 관리하는 코드를 짤 때 필연적으로 따라와야 하는 것이 있습니다. 메모리에 새로운 공간을 만들어 어떤 값에 할당해주었다면, 그 값을 다 쓴 뒤에 시스템에게 그 값을 다 썼다고 알려주어야 합니다. 그렇지 않으면 더 이상 쓰지 않는 쓰레기 값이 메모리에 저장되어 낭비될 것이기 때문입니다.

    이렇게 더 이상 쓰지 않는 메모리 공간을 다른 작업에 활용할 수 있도록 해주는 과정을 메모리를 Deallocate한다, 혹은 메모리를 Free한다 라고 합니다. 그리고 이렇게 쓰지 않는 메모리를 풀어주는 것을 메모리를 관리한다고 부릅니다.

    C, C++ 등의 프로그램은 Manual Management 방식을 사용합니다. free 등의 명령어를 사용하여 사용한 메모리를 직접 풀어주는 것이죠. 이런 방식은 개발자가 메모리를 직접 관리할 수 있어 효율적이지만, 각종 오류가 발생할 수 있다는 단점이 있습니다.

    반면 Java, Scala 등의 프로그램은 Automatic Management를 지원합니다. 개발자가 메모리를 관리해주는 코드를 따로 넣지 않아도 자동으로 사용이 끝난 메모리를 풀어줍니다. 이 과정 Garbage Collection이라고 합니다. 이 방식은 오류가 덜 난다는 장점은 있지만 개발자가 코드를 짤 때 메모리 관리가 어떤 식으로 진행될 지 예측하기 어렵다는 단점이 있으며, Garbage Collection이 일어나는 만큼 런타임이 길어진다는 단점도 있습니다.

     

    Manual Management 방식을 사용하기 위해서는 메모리를 Deallocate 시켜주는 명령어를 하나 정의해주어야 합니다.

     

    Automatic Management 방식을 사용하기 위해서는 아래와 같은 Garbage Collection 과정을 따라야 합니다.

    1. (적절한 시점에) 프로그램 실행을 중단시킵니다.
    2. 현재 Environment에서 접근할 수 있는 모든 메모리 위치를 체크합니다.
    3. 그렇지 않은 메모리 위치들을 전부 Free 시킵니다.


    예시를 통해 좀 더 자세히 알아보겠습니다. 어느 시점에 프로그램을 중단시켰더니 Environment와 Memory가 아래와 같았다고 생각해 보겠습니다.

    Memory의 5번째 줄에서 l6는 오타입니다. l5가 맞습니다.

    Environment에서 접근 가능한 메모리는 $l_1$, $l_2$입니다. 그리고 $l_2$ 메모리에서 $l_3$ 메모리에 접근하고, $l_3$ 메모리는 $l_4$를, $l_4$는 $l_5$에 접근하므로 $l_1$부터 $l_5$까지가 접근 가능한 메모리가 됩니다. 따라서, Garbage Collection 이후 나머지 메모리 위치는 전부 Memory에서 삭제됩니다.

    역시 l6는 오타입니다. l5가 맞습니다.

     

    감사합니다.


    이 글은 컴퓨터공학과 학부생이 개인 공부 목적으로 작성한 글이므로, 부정확하거나 틀린 내용이 있을 수 있으니 참고용으로만 봐주시면 좋겠습니다. 레퍼런스 및 글에 대한 기본적인 정보는 이 글을 참고해 주세요.

    '2023-2학기 > 프로그래밍언어' 카테고리의 다른 글

    [프언] #11. Type System  (0) 2023.12.04
    [프언] #10. Concrete/Abstract Syntax  (1) 2023.11.20
    [프언] #08. States  (0) 2023.10.19
    [프언] #07. Lexical Scoping of Variables  (0) 2023.10.17
    [프언] #06. Procedures  (0) 2023.10.16
Designed by Tistory.